• 【NOIP2000】方格取数 DP优化 解题报告


    问题 L(1137): 【NOIP2000】方格取数

    时间限制: 1 Sec  内存限制: 64 MB

    题目描述

    设有N*N的方格图,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。如下图所示(见样例):  

    某人从图的左上角的A 点出发,可以向下行走,也可以向右走,直到到达右下角的B点。

    在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。

    此人从A点到B 点共走两次,试找出2条这样的路径,使得取得的数之和为最大。

    输入

    第1行:1个整数N(N<=10),表示N*N的方格图,

    第2..?行:每行有3个整数,前2个表示某个方格的位置,第3个数为该位置上所放的数。

    一行单独的0表示输入结束。

    输出

    第1行:1个整数,表示2条路径上取得的最大的和。

    样例输入

    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
    

    样例输出

    67


    这道题特点是要解题时同时顾及两个人

    一个一个人地贪心肯定是行不通的


    本题正解应该是dp,因为要同时考虑到两个人


    于是设状态f[x1][y1][x2][y2]为第一个人在(x1,y1),第二个人在(x2,y2)时能达到的最大答案

    用g[x][y]表示格子(x,y)上的数字,bool b=(x1==x2)&&(y1==y2);


    状态转移:

    1.两个人同时向右走:

    f[x1][y1][x2][y2]=max(f[x1][y1][x2][y2],f[x1-1][y1][x2-1][y2]+g[x1][y1]+b?0:g[x2][y2]);

    2.两个人同时向下走:

    f[x1][y1][x2][y2]=max(f[x1][y1][x2][y2],f[x1][y1-1][x2][y2-1]+g[x1][y1]+b?0:g[x2][y2]);

    3.两个人分别向右和向下走

    f[x1][y1][x2][y2]=max(f[x1][y1][x2][y2],f[x1-1][y1][x2][y2-1]+g[x1][y1]+b?0:g[x2][y2]);

    4.两个人分别向下和向右走

    f[x1][y1][x2][y2]=max(f[x1][y1][x2][y2],f[x1][y1-1][x2-1][y2]+g[x1][y1]+b?0:g[x2][y2]);


    由于状态总共有n^4种,转移是常数,所以时间复杂度为O(n^4)


    有没有更好的方法呢?

    可以看出x1+y1==x2+y2==l

    换种说法,每个状态的两个人总是在同一条对角线上

    于是可以枚举对角线,x1和x2,从而算出y1和y2。

    设状态f[l][x1][x2]为在从右到左第l-1条对角线上,第一个人横坐标为x1,第二个人横坐标为x2

    状态转移与之前类似

    由于对角线只有2*n条

    于是时间复杂度缩减为O(n^3)



    下面贴优化后代码

    #include<cstdio>
    int max(int a,int b){return a<b?b:a;}
    int min(int a,int b){return a>b?b:a;}
    int n;
    int f[24][12][12];
    int g[12][12];
    int main(){
    	scanf("%d",&n);
    	int ag1,ag2,ag3;
    	while(scanf("%d%d%d",&ag1,&ag2,&ag3)&&ag1&&ag2&&ag3)
    		g[ag1][ag2]=ag3;
    	int lmt=n*2;
    	f[2][1][1]=g[1][1];
    	for(int i=3;i<=lmt;i++){
    		int c=min(i,n+1);
    		int s=i>n?i-n:1;
    		for(int j=s;j<c;j++)
    			for(int k=s;k<c;k++){
    				int x1=j,x2=k,y1=i-j,y2=i-k;
    				bool b=(x1==x2&&y1==y2);
    				int tmp=max(max(f[i-1][x1-1][x2-1],f[i-1][x1][x2]),
    				max(f[i-1][x1-1][x2],f[i-1][x1][x2-1]));
    				f[i][x1][x2]=max(f[i][x1][x2],tmp+g[x1][y1]+(b?0:g[x2][y2]));
    				//多加了个括号就对了!
    				//printf("f[%d][%d][%d]:%d,tmp:%d
    ",i,x1,x2,f[i][x1][x2],tmp);
    				//getchar();
    			}
    		}
    	printf("%d
    ",f[lmt][n][n]);
    }


  • 相关阅读:
    pku2992 Divisors
    pku3090 Visible Lattice Points
    pku3615 Cow Hurdles
    禁止 verifier.dll 监控程序
    运行ogreSDK的samples
    #pragma pack(n) 啥时候可以不再忘记?
    游戏开发流程图。
    希望可以尽快的写篇自己的成果。
    windows与OS X下的std::string
    VS2008鼠标右键不灵敏,TFS的Local Path无法打开对应文件夹
  • 原文地址:https://www.cnblogs.com/Hineven/p/5843573.html
Copyright © 2020-2023  润新知