uva 10913 - Walking on a Grid

来源:互联网 发布:微缩景观世界 知乎 编辑:程序博客网 时间:2024/05/03 22:07

4th IIUC Inter-University Programming Contest, 2005

I

Walking on a Grid

Input: standard input
Output: standard output

Problemsetter: Sohel Hafiz

You will be given a square grid of size N × N. The top-left square has a coordinate of (1, 1) and that of bottom-right is (N, N). Your job is to walk from (1, 1) to (N, N). Very easy, right? That’s why you have to follow some rules when walking.

  1. You can only move left, right or down.
  2. (i, j-1) is left of (i, j), (i, j+1) is right of (i, j) and (i+1, j) is down of (i, j).
  3. You can never move outside the grid.
  4. You can not step on a cell more than once.
  5. Every cell has an integer associated with it.
  6. You have to make sure the sum of integers of the path is maximized.
  7. You can step on at most negative integers from source to destination.

Input

Each case will start with two integers N and kN ≤ 75 and k ≤ 5. Each of the next N lines will containN integers each given in row major order. That is, the first integer of the first row is (1, 1) and the last integer of last row is (N, N). Input terminates with two zeros on a line.

Output

For every case output the case number. If it’s not possible to reach the destination meeting the above rules then output “impossible”, else print the maximum sum of integers of the path.

Sample Input

Output for Sample Input

4 1
1 2 3 -5
-10 6 0 -1
-10 -10 -10 2
0 0 0 1
4 0
1 2 3 -5
-10 6 0 -1
-10 -10 -10 2
0 0 0 1
0 0

Case 1: 11
Case 2: impossible


这道题说到底还是数字三角形,只是多加了一些限制条件。如果采用递推,要从底层往上推,dp[i][j][k]表示从(i,j)到(n,n)经过k个负数,包括(i,j)和(n,n)这两点,需要预处理一些信息,比如区间和,区间负数个数,都可用前缀思想。


代码:

#include<cstdio>#include<iostream>#define Maxn 80using namespace std;int sum[Maxn][Maxn],cou[Maxn][Maxn],mat[Maxn][Maxn],dp[Maxn][Maxn][6];const int inf=-1<<30;int main(){    int n,m,cas=1;    while(scanf("%d%d",&n,&m),n||m){        for(int i=1;i<=n;i++)            for(int j=1;j<=n;j++){                scanf("%d",&mat[i][j]);                sum[i][j]=sum[i][j-1]+mat[i][j];                cou[i][j]=mat[i][j]<0?cou[i][j-1]+1:cou[i][j-1];            }        for(int i=1;i<=n;i++)            for(int j=1;j<=n;j++)                for(int k=0;k<=m;k++)                    dp[i][j][k]=inf;        for(int j=1;j<=n;j++)            for(int k=0;k<=m;k++)                if(cou[n][n]-cou[n][j-1]==k)                    dp[n][j][k]=sum[n][n]-sum[n][j-1];        for(int i=n-1;i>0;i--)            for(int j=1;j<=n;j++)                for(int k=0;k<=m;k++){                    for(int s=1;s<=j;s++){                        int tmp=k-cou[i][j]+cou[i][s-1];                        if(tmp>=0&&dp[i+1][s][tmp]!=inf){                            dp[i][j][k]=max(dp[i][j][k],dp[i+1][s][tmp]+sum[i][j]-sum[i][s-1]);                        }                    }                    for(int s=j+1;s<=n;s++){                        int tmp=k-cou[i][s]+cou[i][j-1];                        if(tmp>=0&&dp[i+1][s][tmp]!=inf){                            dp[i][j][k]=max(dp[i][j][k],dp[i+1][s][tmp]+sum[i][s]-sum[i][j-1]);                        }                    }                }        int ans=inf;        for(int i=0;i<=m;i++)            ans=max(ans,dp[1][1][i]);        printf("Case %d: ",cas++);        if(ans==inf) puts("impossible");        else printf("%d\n",ans);    }    return 0;}

以上这段程序跑了0.046s,下面我还用记忆化写了一段程序,竟然跑了0.035,从原理上讲递归肯定花时间的,实际上确实是这样的,我的那段递推代码其实偷了点懒,实际上那重s循环还能够缩小范围的,最好是从当前点向两边辐射,当不满足条件时,直接break,要知道这重for是嵌套在第 4层的,如果优化一下时间肯定大大缩短,但为了降低编程难度就偷懒了,因此记忆化就显示了这个特点。

这里用4维数组记录状态,dp[i][j][k][dir]表示从(i,j)到(n,n)经过k个负数,包括(i,j)和(n,n)这两点,并且在转移到(i,j)这点的方向是dir,这直接影响(i,j)的可行转移,并且使搜索有了单向性。因为搜索时按单格递推的,所以不需要预处理信息了,而递推是整行扫描的,所以要预处理信息。


代码:

#include<cstdio>#include<iostream>#include<cstring>using namespace std;int dx[]={0,1,0};int dy[]={-1,0,1};int dp[80][80][6][3],mat[80][80];int n,m;const int inf=0x88888888;int dfs(int x,int y,int k,int dir){    int &ans=dp[x][y][k][dir];    if(ans!=inf) return ans;    if(x==n&&dir==0) return ans=-inf;    if(x==n&&y==n){        if(mat[n][n]<0&&k==1||mat[n][n]>=0&&k==0)            return ans=mat[n][n];        return ans=-inf;    }    for(int i=0;i<3;i++){        int tx=x+dx[i],ty=y+dy[i];        if(dir==0&&i==2) continue;        if(dir==2&&i==0) continue;        if(tx<1||tx>n||ty<1||ty>n) continue;        int kk=mat[x][y]<0?k-1:k;        if(kk<0) continue;        if(dfs(tx,ty,kk,i)!=-inf)            ans=max(ans,dp[tx][ty][kk][i]+mat[x][y]);    }    if(ans==inf) ans=-inf;    return ans;}int main(){    int cas=1;    while(scanf("%d%d",&n,&m),n||m){        for(int i=1;i<=n;i++)            for(int j=1;j<=n;j++)                scanf("%d",&mat[i][j]);        memset(dp,0x88,sizeof dp);        printf("Case %d: ",cas++);        int ans=inf;        for(int i=0;i<=m;i++){            if(dfs(1,1,i,1)==-inf) continue;            ans=max(ans,dfs(1,1,i,1));        }        if(ans==inf) puts("impossible");        else printf("%d\n",ans);    }    return 0;}


0 0
原创粉丝点击