Labyrinth(Dp)2014年百度之星 资格赛

来源:互联网 发布:java编写口令红包 编辑:程序博客网 时间:2024/05/01 02:37

Labyrinth

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) 
Total Submission(s): 519    Accepted Submission(s): 174 

Problem Description

度度熊是一只喜欢探险的熊,一次偶然落进了一个m*n矩阵的迷宫,该迷宫只能从矩阵左上角第一个方格开始走,只有走到右上角的第一个格子才算走出迷宫,每一次只能走一格,且只能向上向下向右走以前没有走过的格子,每一个格子中都有一些金币(或正或负,有可能遇到强盗拦路抢劫, 度度熊身上金币可以为负,需要给强盗写欠条 ),度度熊刚开始时身上金币数为0,问度度熊走出迷宫时候身上最多有多少金币?

Input

输入的第一行是一个整数T(T < 200),表示共有T组数据。 每组数据的第一行输入两个正整数m,n(m<=100,n<=100)。接下来的m行,每行n个整数,分别代表相应格子中能得到金币的数量,每个整数都大于等于-100且小于等于100。

Output

对于每组数据,首先需要输出单独一行”Case #?:”,其中问号处应填入当前的数据组数,组数从1开始计算。 每组测试数据输出一行,输出一个整数,代表根据最优的打法,你走到右上角时可以获得的最大金币数目。

Sample Input

23 41 -1 1 02 -2 4 23 5 1 -902 21 11 1

Sample Output

Case #1:18Case #2:4

------------------------------------------------------------------------------------------------

伤不起,同学告诉我是搜索,我用dfs+dp做了4个小时,答案正确,但一直超时,各种剪枝都试了,也无法ac。

还是对dfs的时间复杂度掌握不牢。

dfs代码如下:

#include<stdio.h>long map[107][107],n,m;int play[3][2]={1,0,0,1,-1,0};struct{  long ans,step;     }f[107][107];void dfs(int x,int y){     int i,dx,dy;     for(i=0;i<3;i++)     {       dx=x+play[i][0];       dy=y+play[i][1];       if(dx>0 && dy>0 && dx<=m && dy<=n && f[x][y].ans+map[dx][dy]>f[dx][dy].ans && f[dx][dy].step>=f[x][y].step)         {          f[dx][dy].ans=f[x][y].ans+map[dx][dy];          f[dx][dy].step=f[x][y].step+1;          dfs(dx,dy);         }     }}main(){  int t;  while(scanf("%d",&t)!=EOF)    {      int hi=1;      while(t--)      {        int i,j,a,b;        scanf("%d %d",&m,&n);        for(i=1;i<=m;i++)          for(j=1;j<=n;j++)             {scanf("%d",&map[i][j]);f[i][j].ans=-101;f[i][j].step=9999999;}        f[1][1].ans=map[1][1];        f[1][1].step=0;        dfs(1,1);        printf("Case #%d:\n",hi++);        printf("%ld\n",f[1][n].ans);                            }    }}

冷静一下,其实100的迷宫不算大,也没有复杂的元素干扰,是不是考虑贪心?

尝试贪心模拟测试数据,发现可以用dp。每一格的金钱数,必然从3个状态转移而来。

举例:

1 -1 1 0

2 -2 4 2

3 5 1 -90

dp[i][j]代表了地图上坐标为i,j的最多金钱数,那么dp矩阵的第一列必然是136

因为第一列只能自上而下的移动。如果只考虑向右移动,第二列可能是0 1 11 对于这一列,有可能还存在往上或者往下移动的可能,那么我就把所有可能全列举出来,并取最大值。注意防止回头走重复的路陷入死循环,程序里我用了一个变量tmp来防止走回来。

题目先输入m再输入n着实烦,我还是改回来了。ac代码:

#include<stdio.h>int map[107][107],n,m,dp[107][107];int max(int a,int b){    if(a>b)return a;    else return b;}void DP(int c){  for(int i=1;i<=n;i++)//自上而下   {    int tmp=dp[i][c-1]+map[i][c];// tmp是用来记录从左边来的值     dp[i][c]=max(dp[i][c],tmp);    for(int j=i+1;j<=n;j++)      {        tmp=tmp+map[j][c];        dp[j][c]=max(dp[j][c],tmp);      }  }  for(int i=n;i>=1;i--)//自下而上   {    int tmp=dp[i][c-1]+map[i][c];    dp[i][c]=max(dp[i][c],tmp);    for(int j=i-1;j>=1;j--)      {        tmp=tmp+map[j][c];        dp[j][c]=max(dp[j][c],tmp);      }  }}main(){  int t;  while(scanf("%d",&t)!=EOF)    {      int hi=1;      while(t--)      {        int i,j,a,b;        scanf("%d %d",&n,&m);        for(i=1;i<=n;i++)          for(j=1;j<=m;j++)             {scanf("%d",&map[i][j]);dp[i][j]=-999999;}          dp[1][1]=map[1][1];       //初始化第一列        for(i=2;i<=n;i++)          dp[i][1]=dp[i-1][1]+map[i][1];        for(i=2;i<=m;i++)          DP(i);        printf("Case #%d:\n",hi++);        printf("%d\n",dp[1][m]);                 }    }}


0 0
原创粉丝点击