题目来源: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 }