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.
- You can only move left, right or down.
- (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).
- You can never move outside the grid.
- You can not step on a cell more than once.
- Every cell has an integer associated with it.
- You have to make sure the sum of integers of the path is maximized.
- You can step on at most k negative integers from source to destination.
Input
Each case will start with two integers N and k. N ≤ 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;}
- uva 10913 Walking on a Grid
- UVA 10913 Walking on a Grid
- Uva 10913 - Walking on a Grid
- UVa 10913 - Walking on a Grid dp
- UVA - 10913 Walking on a Grid
- UVa:10913 Walking on a Grid
- uva 10913Walking on a Grid
- uva 10913 Walking on a Grid
- Walking on a Grid - UVa 10913 dp
- uva 10913 - Walking on a Grid
- UVA 10913 - Walking on a Grid
- uva 10913 Walking on a Grid
- UVA - 10913 Walking on a Grid
- UVA - 10913 Walking on a Grid
- uva 10913Walking on a Grid
- 10913 - Walking on a Grid
- 10913 - Walking on a Grid
- Uva 10913 - Walking on a Grid 记忆搜索
- 我的编程之路(二十) 新的环境、新的开始
- Longest Increasing Subsequence
- LeetCode-Two Sum
- Android:可动态布局抽屉之基础
- 远程视频监控之构思篇
- uva 10913 - Walking on a Grid
- mysql数据库的基础知识
- hdu1078 FatMouse and Cheese
- LeetCode-Minimum Window Substring
- Android:可动态布局抽屉之完整篇
- Pseudo-variables in Watch Windows
- leetcode 刷题之路 65 Symmetric Tree
- VC++网络高级编程
- Cloudera Manager 5.1.1 离线安装