P1510 精卫填海

事情是这样的,这几天我都一直没怎么刷题(好颓。。),直到今天上午去机房扯淡的时候小张同学跑来跟我说:我精卫填海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就好了,追求代码简洁的同学可以试试这样做(你试试伐,我怕我这个想法是错的

以上仅是个人对于这道题的全部思路与想法,如果有什么不对的地方,还请各位大佬及时向我纠正。

原文地址:https://www.cnblogs.com/dgdger/p/13157290.html