题目大意:给出一个矩阵,要求求出从(1,1)走到(n,n)这个位置的路途上的数字的最大和,走的时候有限制,同一个位置不能走两次,有三个移动方向,下,左,右,还有一个限制,给出一个k,要求走过位置是负数的不能超过k
解题思路一:用一个四维数组来表示状态,dp[i][j][k][l]表示(i,j)位置,走了k个负数位置,移动方向是l
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
#define maxn 80
#define MIN -((LL)1<<60)
int N,K;
int mov[3][2] = {{0,1},{1,0},{0,-1}};
bool vis[maxn][maxn][7][3],vis2[maxn][maxn];
long long dp[maxn][maxn][7][3],arr[maxn][maxn];
bool check(int x,int y) {
return x >= 1 && x <= N && y >= 1 && y <= N;
}
long long solve(int x, int y, int z, int r) {
bool &flag = vis[x][y][z][r];
long long &res = dp[x][y][z][r];
if(flag)
return res;
else if( (x == N && y == N) || z == 0) {
flag = 1;
if(z == 0)
res = MIN;
else
res = arr[x][y];
return res;
}
else {
res = MIN;
for(int i = 0; i < 3; i++) {
int xx = x + mov[i][0];
int yy = y + mov[i][1];
int t = 0;
if(arr[x][y] < 0)
t = - 1;
if(check(xx,yy) && !vis2[xx][yy]) {
if((r == 0 && i == 2) || (r == 2 && i == 0))
continue;
vis2[xx][yy] = 1;
long long temp = solve(xx,yy,z+t,i);
if(temp != MIN)
res = max(res,temp+arr[x][y]);
vis2[xx][yy] = 0;
}
}
flag = 1;
return res;
}
}
int main() {
int mark = 1;
while(scanf("%d%d",&N,&K) != EOF && N+K) {
for(int i = 1; i <= N; i++)
for(int j = 1; j <= N; j++)
scanf("%lld", &arr[i][j]);
if(arr[N][N] < 0)
K--;
memset(vis,0,sizeof(vis));
memset(vis2,0,sizeof(vis2));
long long res = solve(1,1,K+1,1);
if(res != MIN)
printf("Case %d: %lld\n",mark++,res);
else
printf("Case %d: impossible\n",mark++);
}
return 0;
}
解法二:从上往下扫描,然后分别从左往右扫描每一行和从右往左扫描每一行,并进行比较,取最大值
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 80
#define MIN -0x3f3f3f3f
int N,k;
int arr[maxn][maxn];
int dp[maxn][maxn][maxn],left[maxn][maxn],right[maxn][maxn];
void solve() {
for(int i = 0 ; i <= k; i++)
for(int j = 1; j <= N; j++)
for(int l = 1; l <= N; l++)
dp[i][j][l] = MIN;
if(arr[1][1] >= 0)
dp[0][1][1] = arr[1][1];
else
dp[1][1][1] = arr[1][1];
for(int i = 0; i <= k; i++)
for(int j = 1; j < N; j++)
if(dp[i][1][j] != MIN) {
if(arr[1][j+1] < 0) {
if(dp[i][1][j] + arr[1][j + 1] > dp[i+1][1][j+1])
dp[i+1][1][j+1] = dp[i][1][j] + arr[1][j+1];
}
else if (dp[i][1][j] + arr[1][j+1] > dp[i][1][j+1])
dp[i][1][j+1] = dp[i][1][j] + arr[1][j+1];
}
for(int l = 2; l <= N; l++) {
for(int i = 0; i <= k; i++)
for(int j = 1; j <= N; j++)
if(dp[i][l-1][j] != MIN) {
if(arr[l][j] < 0) {
if(dp[i][l-1][j] + arr[l][j] > dp[i+1][l][j])
dp[i+1][l][j] = dp[i][l-1][j] + arr[l][j];
}
else if(dp[i][l-1][j] + arr[l][j] > dp[i][l][j])
dp[i][l][j] = dp[i][l-1][j] + arr[l][j];
}
for(int i = 0; i <= k; i++)
for(int j = 1; j <= N; j++)
left[i][j] = right[i][j] = dp[i][l][j] ;
for(int i = 0; i <= k; i++)
for(int j = 1; j < N; j++)
if(left[i][j] != MIN) {
if(arr[l][j+1] < 0) {
if(left[i][j] + arr[l][j+1] > left[i+1][j+1])
left[i+1][j+1] = left[i][j] + arr[l][j+1];
}
else if (left[i][j] + arr[l][j+1] > left[i][j+1])
left[i][j+1] = left[i][j] + arr[l][j+1];
}
for(int i = 0; i <= k; i++)
for(int j = N; j > 1; j--)
if(right[i][j] != MIN) {
if(arr[l][j-1] < 0) {
if(right[i][j] + arr[l][j-1] > right[i+1][j-1])
right[i+1][j-1] = right[i][j] + arr[l][j-1];
}
else if (right[i][j] + arr[l][j-1] > right[i][j-1])
right[i][j-1] = right[i][j] + arr[l][j-1];
}
for(int i = 0 ; i <= k; i++)
for(int j = 1; j <= N; j++)
dp[i][l][j] = left[i][j] > right[i][j] ? left[i][j] :right[i][j];
}
}
int main() {
int mark = 1;
while(scanf("%d%d",&N,&k) != EOF && N+k) {
for(int i = 1; i <= N; i++)
for(int j = 1; j <= N; j++)
scanf("%d",&arr[i][j]);
solve();
int max = MIN;
for(int i = 0; i <= k; i++)
if(dp[i][N][N] > max)
max = dp[i][N][N];
if(max != MIN)
printf("Case %d: %d\n",mark++,max);
else
printf("Case %d: impossible\n",mark++);
}
return 0;
}