事情是这样的,这几天我都一直没怎么刷题(好颓。。),直到今天上午去机房扯淡的时候小张同学跑来跟我说:我精卫填海A掉了!
我本来觉得这题很难很难很难,然后就给自己找各种理由不做,可是看到小张同学精神这么抖擞,那我也不能落后啦。
一开始觉得还蛮难的,但是经过我在语文课上仔细思考后,得出了动态转移方程。
思路如下:
小张同学的一句话启发了我:体力为x时最大的体积
然后我灵光一闪(其实是思考了一整节语文课。。)推出了动态转移方程:f[i][j]=max(f[i-1][j],f[i-1][j-tili[i]]+tiji[i])
tili这个数组存的当然是体力了,tiji存的是体积
然后i是第i个石头,j是当前的体力
可是这个二维数组看起来有点晕乎乎的,我们通过观察可以发现max函数里面的两个的前面的那个都是i-1,
也就相当于一样嘛,那他一直一样我们还要他干什么,就直接写一维数组就好,这样更简便而且更好理解:f[j]=max(f[j],f[j-tili[i]]+tiji[i])
方程有了,代码也就好写了:
for(int i=0;i<n;i++){ for(int j=c;j>=0;j--){ if(j>=tili[i]){//判断当前体力是否大于石头的体力 f[j]=max(f[j],f[j-tili[i]]+tiji[i]);//动态转移方程 } } }
我们既然存了这个最大的体力值,那我们就要用了,一个for遍历一边把体积大于v而且使用最小的体力值算出来就好了。
可是当我写的时候发现:我根本没有存体力啊。
那怎么办?
俗话说的好,用struct准没错
struct lilili{//名字比较奇怪不要介意哈 int li,ji; }f[10010];//定义struct数组
既然我们把这个数组改了,for嵌套当然也要改
(大家不要学习哈,这是我十分的代码,写代码的时候一定要考虑周全,不然会像我一样出事故
for(int i=0;i<n;i++){ int q=0;//建立小旗子 for(int j=c;j>=0;j--){ if(j>=tili[i]){ f[j].ji=max(f[j].ji,f[j-tili[i]].ji+tiji[i]);//动态转移方程 if(f[j].ji==f[j].ji){//如果他选择的前面的一个,体力就等于前面的体力+搬这块石头需要的体力 if(q==0) f[j].li=f[j].li+tili[i];//因为怕有一直是f[j].ji大的情况,只能加一次(不然就会多算很多体力),所以我们用一个小旗把他标记起来 } else if(f[j].ji==f[j-tili[i]].ji+tiji[i]) f[j].li=f[j-tili[i]].li+tili[i];//如果是后面的那个大,我们就用他之前搬的体力+搬这块石头需要的体力 } } }
可是眼尖心细的同学一定会发现我这里面有许多错误,这也就是我为什么只得了10分的原因
那么我们现在一个一个来改正:
1.if(f[j].ji==f[j].ji)(上面代码的第6行)
这个判断肯定是成立的,他自己一定等于他自己。
我们想让他判断的前一个f[j].ji已经不是以前的f[j].ji了,所以我们需要一个变量用来储存前一个数,再在判断的时候用这个数就好了
2.if(q==0)(上面代码第7行)
我在这个判断里面根本没有让q=1啊!
所以这个小旗子没有用啊!我们在判断里面加一句:q=1就ok了
(好像也不是很多哈
for(int i=0;i<n;i++){ int q=0,qian=0; for(int j=c;j>=0;j--){ if(j>=tili[i]){ qian=f[j].ji;//每次记录哦 f[j].ji=max(qian,f[j-tili[i]].ji+tiji[i]);//这里也直接用qian这个变量来判断就好了,省的写这么麻烦 if(f[j].ji==qian){ if(q==0){ f[j].li+=tili[i]; q=1;//q=1哦 } } else if(f[j].ji==f[j-tili[i]].ji+tiji[i]) f[j].li=f[j-tili[i]].li+tili[i]; } } }
最后再来一个for遍历数组取最小的体力值输出就行啦
for(int i=c;i>=0;i--){ if(f[i].ji>=v){ minn=min(f[i].li,minn);//minn取需要用的最小的体力值 xq=1;//小旗是在main外面定义的,用来记录这些石头可不可以将这个池子填满的 } } if(xq==1) printf("%d ",c-minn);//如果这个石头可以将这个池子填满 else printf("Impossible");//如果不行
下面是完整代码:
#include<iostream> #include<cstdio> using namespace std; int v,n,c,tili[10010],tiji[10010],minn=999999,xq=0; struct lilili{ int li,ji; }f[10010]; int main(){ scanf("%d%d%d",&v,&n,&c); for(int i=0;i<n;i++){ scanf("%d%d",&tiji[i],&tili[i]); } for(int i=0;i<n;i++){ int q=0,qian=0; for(int j=c;j>=0;j--){ if(j>=tili[i]){ qian=f[j].ji; f[j].ji=max(qian,f[j-tili[i]].ji+tiji[i]); if(f[j].ji==qian){ if(q==0){ f[j].li+=tili[i]; q=1; } } else if(f[j].ji==f[j-tili[i]].ji+tiji[i]) f[j].li=f[j-tili[i]].li+tili[i]; } } } for(int i=c;i>=0;i--){ if(f[i].ji>=v){ minn=min(f[i].li,minn); xq=1; } } if(xq==1) printf("%d ",c-minn); else printf("Impossible"); return 0; }
代码已经在上面解释过了,上面就不加注释了(就是懒!
当然我A掉这道题之后再去看我的代码发现有很多啰啰嗦嗦的地方,比如说根本不用存前一个,直接把后面的判断移到前面来,后面加个else就好了,追求代码简洁的同学可以试试这样做(你试试伐,我怕我这个想法是错的