【2018集訓隊互測】【XSY3372】取石子

題目來源:2018集訓隊互測 Round17 T2ios

題意:

題解:

顯然我是不可能想出來的……可是以爲這題題解太神了就來搬(chao)一下……Orzpyz!spa

顯然不會無解……3d

爲了方便計算石子個數,在最後面加一堆$a_i=c_i=\infty$的石子,確保每次取石子均可以取滿$k$個;code

先考慮$a_i=0$的狀況:orm

設$f_{i,j}$表示只考慮第0到$i$堆石子,通關前$j$輪的最少操做次數;blog

設$g_{i,j}$表示只考慮第0到$i$堆石子,前$j$輪結束後再取若干次石子,每次取$k$個,使得第$i$堆前面的全部石堆都被取盡的最少操做次數;string

分別記$sa,sb$爲$a,b$的前綴和;it

從小到大枚舉$i,j$,每次考慮加入石堆$i$的影響,分兩種狀況轉移:io

若存在一種方案使得不取第$i$堆石子便可通關前$j$輪,此時需知足$j\times b_i\leq c_i$且$f_{i-1,j}\neq\infty$;form

則轉移爲:

$f_{i,j}\leftarrow f_{i-1,j}$

$g_{i,j}\leftarrow \lceil\frac{j\times sb_{i-1}}{k}\rceil$

第二種轉移須要知足石子總數夠取,即$\lceil\frac{j\times sb_{i-1}}{k}\rceil\times k\leq j\times sb_i$;

不然枚舉最後一次取$i$的輪數$r$,此時最優策略必定是分紅三部分:

1.在前$r$輪取完$[0,i)$中的石子,這部分答案顯然爲$g_{i,r}$

2.這輪在$i$中取若干次石子,此時$i$中剩餘石子數爲$m=r\times sb_i-k\times g_{i,r}$,爲了使它在$j$輪後不超過限制,還須要取$x=\lceil\frac{max(0,m+(j-r)\times b_i-c_i)}{k}\rceil$次;若$x\times k>m$,即石子不夠取,則無解;

3.在剩餘的$j-r$輪中取石子,這部分跟第一種狀況相似,能夠獲得$f$的操做數爲$f_{i-1,j-r}$,$g$的操做數爲$g_{i,j}$;

所以轉移就是:

$f_{i,j}\leftarrow g_{i,r}+f_{i-1,j-r}+x$

$g_{i,j}\leftarrow g_{i,r}+\lceil\frac{(j-r)\times sb_{i-1}}{k}\rceil+x$

時間複雜度$O(nt^2)$;

考慮$a_i\neq 0$的狀況,能夠發現此時對dp的影響只有第二種狀況的第三步中,全部石堆的個數都爲0;那麼直接在$f,g$後面加一維0/1位表示每堆石子初始時是否有值,在第三步轉移中用$f_{i-1,j-r,0}$轉移,每次計算剩餘石子時注意初始的$a_i$便可;

代碼看(chao)了天下第一wxh的代碼

代碼:

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<queue>
 7 #define inf 100000000000000000
 8 #define eps 1e-9
 9 using namespace std; 10 typedef long long ll; 11 typedef double db; 12 int n,t,k; 13 ll tmp,tt,m,a[202],b[202],c[202],sa[202],sb[202],f[202][202][2],g[202][202][2]; 14 int main(){ 15     scanf("%d%d%d",&n,&t,&k); 16     for(int i=1;i<=n;i++){ 17         scanf("%lld%lld%lld",&a[i],&b[i],&c[i]); 18         sa[i]=sa[i-1]+a[i]; 19         sb[i]=sb[i-1]+b[i]; 20  } 21     n++; 22     a[n]=c[n]=sa[n]=inf; 23     sb[n]=sb[n-1]; 24     for(int i=1;i<=n;i++){ 25         for(int j=0;j<=t;j++){ 26             for(int t=0;t<=1;t++){ 27                 f[i][j][t]=g[i][j][t]=inf; 28                 //transform 1
29                 if(j*b[i]+t*a[i]<=c[i]&&f[i-1][j][t]!=inf){ 30                     f[i][j][t]=f[i-1][j][t]; 31                     tmp=(j*sb[i-1]+t*sa[i-1]+k-1)/k; 32                     if(tmp*k<=j*sb[i]+t*sa[i]){ 33                         g[i][j][t]=tmp; 34  } 35  } 36                 //transform 2
37                 for(int r=0;r<j;r++){ 38                     if(g[i][r][t]!=inf){ 39                         m=r*sb[i]+t*sa[i]-k*g[i][r][t]; 40                         tmp=(max(0ll,m+(j-r)*b[i]-c[i])+k-1)/k; 41                         if(tmp*k<=m&&f[i-1][j-r][0]!=inf){ 42                             f[i][j][t]=min(f[i][j][t],f[i-1][j-r][0]+g[i][r][t]+tmp); 43                             tt=((j-r)*sb[i-1]+k-1)/k; 44                             if(tt*k<=(j-r)*sb[i]+m-tmp*k){ 45                                 g[i][j][t]=min(g[i][j][t],g[i][r][t]+tmp+tt); 46  } 47  } 48  } 49  } 50  } 51  } 52  } 53     printf("%lld",f[n][t][1]); 54     return 0; 55 }
相關文章
相關標籤/搜索