00032:Problem A 地宫寻宝
-
总时间限制:
- 1000ms 内存限制:
- 65536kB
-
描述
-
Hujie在普吉岛观摩2016年ACM-ICPC World Final期间,从当地人手中得到几张藏宝图。图上画的是岛上的若干座矩形地下城堡,城堡每个房间都是正方形的且藏有宝藏。城堡南北向有N个房间,东西向有M个房间,且相邻的房间是相通的。当地人告诉他,由于是地下城堡氧气含量稀少,一直没人敢贸然进去。Hujie想到了一种方法,携带氧气瓶从西北角第一个房间(1,1)进入城堡探险,一路向东或向南走,带一些宝藏从东南角的房间(N,M)出来,但是他不知道自己的方案究竟能否可行,能带出来多少宝藏?
输入
-
输入包含多组数据。
每组数据第1行包含3个正整数 N,M,Y (1≤N,M≤100, 0≤Y≤1000),分别表示城堡在南北方向上的房间数N,在东西方向上的房间数M,以及携带的氧气量Y;
紧接着N行,每行M个正整数P ij(0≤P≤10000),表示坐标为(i,j)的房间中的宝藏价值;之后N行,每行M个正整数C ij (0≤C≤1000),表示经过坐标为(i,j)的房间所需要消耗的氧气量。
输入数据以N=M=Y=0结束。
输出
- 对应每一组数据,输出"case #: "表示第#组数据的结果,若能够走出城堡则输出能够携带的最大宝藏价值总和,若不能走出城堡则输出"No way!",每个结果占一行。 样例输入
-
2 2 5
-
1 2
-
3 4
-
1 1
-
1 1
-
2 3 4
-
1 1 1
-
1 1 1
-
2 2 2
-
2 2 2
-
0 0 0
样例输出
-
case 1: 8
-
case 2: No way!
这道题目让我校赛时候十分懵逼啊,当时刚刚学的DP,不是很熟练,就没向那方向去想,只想到了BFS和DFS,但是细思极恐,这题目根本不能用搜索来做,否则情况太多,BFS在20*20的迷宫就爆了,DFS有那么多的情况要去搜索,然后每次比较下max什么的,肯定要超时。
这道题要求是无后效性,前面的情况不能影响后面的情况,因为可能前面你按照宝藏价值高低去走,消耗大量氧气,但是后面基本不消耗氧气,而且宝藏价值更加高,你却没氧气去走了。这约束了你不能用贪心来做,贪心对于acm这种题目十分多变的来说,了解了解也差不多了,基本用不上啊。
对于我一个刚学DP的低端acmer来说,要做一个图的01背包,十分的蛋疼啊,因为图要连通,有方向。最基础的01背包,只需要考虑每个物品加不加而已,这个图就要连起来,而且这道题来说,一加就要加进去了,这是很关键的地方,没有说,我判断(2,2)时候,只取(1,2)这个点的值,存在(2,2)这个点里面,就是说判断max时候,你前面这个点加上自己的价值还没我前面那个可以继承的点高,然后就可以直接选择前面那个点。所以说如果这个点加进来会爆掉氧气的话,这个点就直接舍弃了。
而且我们做得01背包已经是二维数组形式了,这个变成了图,当然要变成三维数组了。
这道题是关键是怎么去舍弃那些不能用的点,就是向下或者向右到这个点,都会爆掉氧气。由于这道题是只能向下或者向右走的,所以我采用的方法很简单,先初始化((1,1)到(1,m))和((1,1)到(n,1)),后面的点全部都是由这两个地方来的,就不需要加什么if去判断新的点是不是在这个范围内了。然后就是一种感觉很有后效性的方法,如果这个点可以加入,那就是正常的值,如果不能用,那就赋值为-1(我这地方一开始赋值为0好直接在&&判断,但是这道题目的数据范围有点蛋疼。。。)
所以说了这么多,到底怎么去舍弃那些不能用的点呢?我的代码如下:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define max(a,b) a>b?a:b
int dp[105][105][1005];
bool G[105][105],flag;
int main()
{
int n,m,y,k,x,i,j,t,ppp=1;
int val[105][105],cost[105][105];
while(scanf("%d%d%d",&n,&m,&y)!=EOF)
{
if(n + m + y == 0)
break;
memset(G,true,sizeof(G));
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&val[i][j]);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&cost[i][j]);
for(i=0;i<=y;i++)
if(i>=cost[1][1])
dp[1][1][i]=val[1][1];
else
dp[1][1][i] = -1;//就是这里,我一开始下面这些都是赋值为0,最后判断最后那个点是不是0,不是就输出,
//但是某些数据正确但是这种写法会错误,全改成-1就正确了
for(i=2;i<=m;i++)
{
for(j=0;j<=y;j++)
if(j>=cost[1][i] && dp[1][i-1][j-cost[1][i]] != -1)
dp[1][i][j] = val[1][i] + dp[1][i-1][j-cost[1][i]];
else
dp[1][i][j] = -1;
}
for(i=2;i<=n;i++)
{
for(j=0;j<=y;j++)
if(j>=cost[i][1] && dp[i-1][1][j-cost[i][1]] != -1)
dp[i][1][j] = val[i][1] + dp[i-1][1][j-cost[i][1]];
else
dp[i][1][j] = -1;
}
for(i=2;i<=n;i++)
{
for(j=2;j<=m;j++)
{
for(k=0;k<2;k++)
{
if(k == 0)
{
for(x=0;x<=y;x++)
{
if(x>=cost[i][j] && dp[i][j-1][x-cost[i][j]] != -1)
{
dp[i][j][x] = val[i][j] + dp[i][j-1][x-cost[i][j]];
}
else
dp[i][j][x] = -1;
}
}
else
{
for(x=0;x<=y;x++)
{
if(x>=cost[i][j])
if(dp[i][j][x])
{
if(dp[i-1][j][x-cost[i][j]] != -1)
dp[i][j][x] = max(val[i][j] + dp[i-1][j][x-cost[i][j]], dp[i][j][x]);
}
else if(dp[i-1][j][x-cost[i][j]] != -1)
dp[i][j][x] = val[i][j] + dp[i-1][j][x-cost[i][j]];
else
dp[i][j][x] = -1;
else
dp[i][j][x] = -1;
}
}
}
}
}
if(dp[n][m][y]!=-1)
printf("case %d: %d\n",ppp++,dp[n][m][y]);
else
printf("case %d: No way!\n",ppp++);
}
return 0;
}
完完全全自己想出来的代码ac了,感觉就是有成就感哈哈,花费了五个小时。