• 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就好了,追求代码简洁的同学可以试试这样做(你试试伐,我怕我这个想法是错的

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

  • 相关阅读:
    Linux命令比较文件内容
    Linux命令jobs小记
    权限控制框架
    Log4j2源码分析系列:(一)配置加载
    Callable和Supplier的区别
    排序算法之归并排序
    Spring boot整合Mybatis
    排序算法之堆排序
    排序算法之希尔排序
    排序算法之快速排序
  • 原文地址:https://www.cnblogs.com/dgdger/p/13157290.html
Copyright © 2020-2023  润新知