题目链接:http://acm.csust.edu.cn/problem/2005
博客园食用链接:https://www.cnblogs.com/lonely-wind-/p/13396928.html
Description
题面太难编了。
如果,你是一只小亚索,你正在峡谷里快乐地释放疾风。更快乐的是小提莫在这里种了好多好多蘑菇。
作为一个优秀的召唤师,你当然要吃完所有的小蘑菇呀。
地图是一个 n ∗ m n∗m n∗m的矩阵 m p [ ] [ ] mp[][] mp[][],初始时(也就是第 0 0 0秒)你在矩阵的左上端点 ( 1 , 1 ) (1,1) (1,1),你的终点是矩阵的右下端点 ( n , m ) (n,m) (n,m),每秒你只能往上下左右 4 4 4个方向移动一格。一个限制:如果这一秒你从格子 x x x走到格子 y y y,下一秒你不能从格子 y y y走到格子 x x x。
初始时整个地图上没有一个蘑菇,如果 m p [ i ] [ j ] mp[i][j] mp[i][j]等于 0 0 0表示这里永远木有蘑菇,否则每过 m p [ i ] [ j ] mp[i][j] mp[i][j]秒这里都会产生一个蘑菇。有一个限制就是蘑菇只能存活 1 1 1秒,我想知道 k k k秒时间内你最多能吃多少个蘑菇,但是第 k k k秒你必须在终点位置。哦对了,如果这场游戏你不能吃够 t t t个蘑菇或者第 k k k秒你走不到终点,你就不是一个合格的队友,就不输出你吃的蘑菇数,输出 h a s h a k i hashaki hashaki。
Input
第一行四个整数,分别表示
n
,
m
,
t
,
k
n,m, t, k
n,m,t,k。
接下来一个 n n n行 m m m列的矩阵 m p mp mp,意义如题。
1 ≤ n , m ≤ 10 , 1 ≤ t , k ≤ 1000 , m p [ i ] [ j ] ≤ 100 1\leq n,m\leq 10,1\leq t,k\leq 1000,mp[i][j]\leq 100 1≤n,m≤10,1≤t,k≤1000,mp[i][j]≤100
Output
输出一行表示答案。
Sample Input 1
2 2 3 6
1 1
1 1
Sample Output 1
6
Sample Input 2
2 2 2 5
1 1
1 1
Sample Output 2
hashaki
Sample Input 3
2 2 4 2
1 1
1 1
Sample Output 3
hashaki
Sample Input 4
2 5 7 19
4 2 1 2 4
10 3 10 9 2
Sample Output 4
7
emmm,应该要想到DP,然后就挺好办的了,由于是方格DP,那么一般会存在状态 d p [ n ] [ m ] dp[n][m] dp[n][m],现在由于有时间的限制,所以应该要枚举时间这个维度,而且时间数据也不大,那么就暗示着可以加在 d p dp dp状态中,于是就有了 d p [ n ] [ m ] [ t ] dp[n][m][t] dp[n][m][t]。然后你还得考虑每次是从哪个方向转移过来的,也就是一定会有方向的枚举,那么就有了 d p [ n ] [ m ] [ t ] [ d i r ] dp[n][m][t][dir] dp[n][m][t][dir]。
似乎也就这四维了,接下来就是构建状态转移方程了,我们第一个枚举的肯定是时间,然后要枚举每个点,再枚举它现在要去哪里(枚举现在的方向),由于方向上的限制,你还得枚举到达该点时的方向,于是就有了代码:
int dx[]={0,-1,0,1,0},dy[]={0,0,-1,0,1};
for (int times=0; times<k; times++) {
for (int i=1; i<=n; i++) {
for (int j=1; j<=m; j++) {
for (int nowdir=1; nowdir<=4; nowdir++) {
for (int lastdir=1; lastdir<=4; lastdir++) {
if (abs(nowdir-lastdir)==2) continue;//往返不符合
int xx=i+dx[nowdir],yy=j+dy[nowdir];
if (!overline(xx,yy,n,m)) continue;
int mash=0;
if (mp[xx][yy] && (times+1)%mp[xx][yy]==0) mash++;
/*DP*/
}
}
}
}
}
既然这些枚举都写出来了,那么状态转移方程也不难得出:
dp[xx][yy][times+1][nowdir]=max(dp[xx][yy][times+1][nowdir],dp[i][j][times][lastdir]+mash);
但需要注意的是每次枚举的上一个方向必须是之前走过的,所以我们要对
d
p
dp
dp进行一下预处理,最后对4个方向上的
d
p
[
n
]
[
m
]
[
k
]
dp[n][m][k]
dp[n][m][k]取个最大值就OK了。
以下是AC代码:
#include <bits/stdc++.h>
using namespace std;
#define overline(x,y,n,m) (x>=1 && x<=n && y>=1 && y<=m)
int mp[20][20];
int dp[11][11][1002][5];
int dx[]={0,-1,0,1,0},dy[]={0,0,-1,0,1};
int main(int argc, char const *argv[])
{
int n,m,t,k;
scanf ("%d%d%d%d",&n,&m,&t,&k);
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++)
scanf ("%d",&mp[i][j]);
memset(dp,-1,sizeof dp);
for (int i=1; i<=4; i++) dp[1][1][0][i]=0;
for (int times=0; times<k; times++){
for (int i=1; i<=n; i++){
for (int j=1; j<=m; j++){
for (int nowdir=1; nowdir<=4; nowdir++){
for (int lastdir=1; lastdir<=4; lastdir++){
if (dp[i][j][times][lastdir]==-1) continue;
if (abs(nowdir-lastdir)==2) continue;
int xx=i+dx[nowdir],yy=j+dy[nowdir];
if (!overline(xx,yy,n,m)) continue;
int mash=0;
if (mp[xx][yy] && (times+1)%mp[xx][yy]==0) mash++;
dp[xx][yy][times+1][nowdir]=max(dp[xx][yy][times+1][nowdir],dp[i][j][times][lastdir]+mash);
}
}
}
}
}
int ans=0;
for (int i=1; i<=4; i++){
ans=max(dp[n][m][k][i],ans);
}
if (ans<t) printf ("hashaki\n");
else printf ("%d\n",ans);
return 0;
}