• luogu P1004 方格取数


    题目描述

    设有 N×NN imes NN×N 的方格图 (N≤9)(N le 9)(N9),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字 000。如下图所示(见样例):

    A
     0  0  0  0  0  0  0  0
     0  0 13  0  0  6  0  0
     0  0  0  0  7  0  0  0
     0  0  0 14  0  0  0  0
     0 21  0  0  0  4  0  0
     0  0 15  0  0  0  0  0
     0 14  0  0  0  0  0  0
     0  0  0  0  0  0  0  0
                             B
    

    某人从图的左上角的 AAA 点出发,可以向下行走,也可以向右走,直到到达右下角的 BBB 点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字 000)。
    此人从 AAA 点到 BBB 点共走两次,试找出 222 条这样的路径,使得取得的数之和为最大。

    输入格式

    输入的第一行为一个整数 NNN(表示 N×NN imes NN×N 的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的 000 表示输入结束。

    输出格式

    只需输出一个整数,表示 222 条路径上取得的最大的和。

    输入输出样例

    输入 #1
    8
    2 3 13
    2 6  6
    3 5  7
    4 4 14
    5 2 21
    5 6  4
    6 3 15
    7 2 14
    0 0  0
    
    输出 #1
    67

    说明/提示

    NOIP 2000 提高组第四题

    第一遍:自己做题时候的思路

    当时的做法其实应该称不上是dp,应该说是贪心?每次选择最优的解,记录经过路径,第一次遍历结束之后把跑过的路径值全部清零,再跑第二次,就可以得到最优解(贪心一定了)。

    (由于数据太弱,这样的做法可以骗到60分)

    #include<bits/stdc++.h>
    using namespace std;
    
    int n;
    int a[10][10];
    
    struct node{
        int last,s,x,y;
    }f[20][20];
    
    int ans=0;
    
    int main()
    {
        cin>>n;
        int p;
        while(cin>>p&&p!=0){
            int y,z;
            cin>>y>>z;
            a[p][y]=z;
        }
    //    cout<<" ****"<<endl;
        
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                f[i][j].s =0;
                f[i][j].last =-1;
            }
            /*
    4
    2 2 3
    2 3 4
    3 1 2
    4 2 1
            */
        
    //    cout<<" ****"<<endl;
        
        
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++){
                if(f[i-1][j].s +a[i][j]>f[i][j-1].s +a[i][j])
                {
                    f[i][j].s =f[i-1][j].s +a[i][j];
                    f[i][j].last =a[i-1][j];
                    f[i][j].x =i-1;
                    f[i][j].y =j; 
                }
                else
                {
                    f[i][j].s =f[i][j-1].s +a[i][j];
                    f[i][j].last =a[i][j-1];
                    f[i][j].x =i;
                    f[i][j].y =j-1;
                }
            }
            
        f[1][1].last =-1;
            
    //    cout<<f[1][1].last <<endl;
    //    cout<<" ****"<<endl;
        ans=f[n][n].s ;
        
        node now=f[n][n];
        int x=f[n][n].x ;
        int y=f[n][n].y ;
    //    cout<<" ****"<<endl;
        while(now.last!=-1&&x!=0&&y!=0)
        {
    //        cout<<x<<" "<<y<<endl;
            a[x][y]=0;
            now=f[x][y];
            x=now.x ;
            y=now.y ;
        }
    //    cout<<" ****5"<<endl;
        
        a[now.x ][now.y ]=0;
        
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                f[i][j].s =0;
                f[i][j].last =-1;
            }
        
        
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++){
                if(f[i-1][j].s +a[i][j]>f[i][j-1].s +a[i][j])
                {
                    f[i][j].s =f[i-1][j].s +a[i][j];
                    f[i][j].last =a[i-1][j];
                    f[i][j].x =i-1;
                    f[i][j].y =j; 
                }
                else
                {
                    f[i][j].s =f[i][j-1].s +a[i][j];
                    f[i][j].last =a[i][j-1];
                    f[i][j].x =i;
                    f[i][j].y =j-1;
                }
            }
            
        ans+=f[n][n].s ;
        
        cout<<ans<<endl;
        return 0;
        
    }

    但是我们仔细想一想就会发现,如果我们只是贪心求解的话,是存在情况不满足这种贪心方式的,比如一开始,我们求出了最长路径,并且成功把最长路径清零了,但是下一次我们就会发现我们有可能把完整路径破坏掉了!就是说一开始我们本可以采取其他的路径走法,使得权值的所有点都可以被遍历到,但是我们人为破坏了这样的路径,最终自然得不到最大路径了。

    第二遍:正解(n^4)

    我们来考虑正解,其实我们发现,题目中总共存在四个状态量。我们完全可以把题目想成这样:有两个人同时走,从起点到达终点,问这两个人的所走路径和最大是多少,我们不妨先将所有状态都设置出来,如f[i][j][k][l]表示第一个人所在位置是(i,j),第二个人所在位置是(k,l),这样的话,f的意思表示为第一个人在(i,j)的位置,第二个人在(k,l)的位置时所经过的最大路径之和,那么f[n][n][n][n]就是我们最后要求的答案

    #include<bits/stdc++.h>
    using namespace std;
    
    int n;
    struct node{
        int a,b,c;
    }s[502];
    int mapa[502][502];
    int f[20][20][20][20];
    
    int main()
    {
        int p=0;
        cin>>n;
        while(1)
        {
            p++;
            cin>>s[p].a >>s[p].b >>s[p].c ;
            if(s[p].a ==0&&s[p].b ==0&&s[p].c ==0) break;
        }
        
        for(int i=1;i<=p;i++)
        {
            mapa[s[i].a ][s[i].b ]=s[i].c ;
        }
        
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                for(int k=1;k<=n;k++)
                    for(int l=1;l<=n;l++){
                        f[i][j][k][l]=max(max(f[i-1][j][k-1][l],f[i-1][j][k][l-1]),max(f[i][j-1][k-1][l],f[i][j-1][k][l-1]))+mapa[i][j]+mapa[k][l];
                        if(i==k&&j==l) f[i][j][k][l]-=mapa[i][j];
                    }
        cout<<f[n][n][n][n]<<endl;
        return 0;
    }    

    第三遍:优化一(n^3)

    其实我们可以把这个状态优化成n^3的复杂度。我们发现我们其实i+j和k+l的值是相同的(因为每次两个人都是只走一步),所以我们完全可以用i+j-k来表示l,这样的话就避免了一层循环

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    struct point
    {
        int x,y,data;
    }p[100];
    int n,m,map[11][11],f[11][11][11][11];
    int main()
    {
        int i,j,k,l;
        scanf("%d",&n);
        while(1)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            if(!a&&!b&&!c)
                break;
            p[++m].x=a;
            p[m].y=b;
            p[m].data=c;
        }
        for(i=1;i<=m;i++)
            map[p[i].x][p[i].y]=p[i].data;
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                for(k=1;k<=n;k++)
                {
                    l=i+j-k;
                    if(l<=0)
                        break;
                    f[i][j][k][l]=max(f[i-1][j][k-1][l],max(f[i-1][j][k][l-1],max(f[i][j-1][k-1][l],f[i][j-1][k][l-1])));
                    if(i==k&&j==l)
                        f[i][j][k][l]+=map[i][j];
                    else
                        f[i][j][k][l]+=map[i][j]+map[k][l];
                }
        printf("%d
    ",f[n][n][n][n]);
        return 0;
    }

    第三遍:优化二(状态压缩),其实和上一个差不多,我们进行状态压缩,f[l][i][ii]表示当前总共走了l步,第一个人在(i,l-i),第二个人在(ii,l-ii),和上面一样进行循环枚举状态就可以

    -----------------end-------------------

  • 相关阅读:
    springboot +mybatis 使用PageHelper实现分页,并带条件模糊查询
    jQuery设置点击选中样式,onmouseover和onmouseout事件
    Ajax跨域设置
    Java获取文章的上一篇/下一篇
    Python str / bytes / unicode 区别详解
    Python bytes 和 string 相互转换
    Python bytearray/bytes/string区别
    Python eval 与 exec 函数区别
    Python eval 与 exec 函数
    Python set list dict tuple 区别和相互转换
  • 原文地址:https://www.cnblogs.com/yxr001002/p/14006741.html
Copyright © 2020-2023  润新知