1. 程式人生 > 實用技巧 >CSUST 2005-修仙(DP)

CSUST 2005-修仙(DP)

題目連結:http://acm.csust.edu.cn/problem/2005
CSDN食用連結:https://blog.csdn.net/qq_43906000/article/details/107662839
Description

題面太難編了。

如果,你是一隻小亞索,你正在峽谷裡快樂地釋放疾風。更快樂的是小提莫在這裡種了好多好多蘑菇。

作為一個優秀的召喚師,你當然要吃完所有的小蘑菇呀。

地圖是一個\(n∗m\)的矩陣\(mp[][]\),初始時(也就是第\(0\)秒)你在矩陣的左上端點\((1,1)\),你的終點是矩陣的右下端點\((n,m)\),每秒你只能往上下左右\(4\)個方向移動一格。一個限制:如果這一秒你從格子\(x\)

走到格子\(y\),下一秒你不能從格子\(y\)走到格子\(x\)

初始時整個地圖上沒有一個蘑菇,如果\(mp[i][j]\)等於\(0\)表示這裡永遠木有蘑菇,否則每過\(mp[i][j]\)秒這裡都會產生一個蘑菇。有一個限制就是蘑菇只能存活\(1\)秒,我想知道\(k\)秒時間內你最多能吃多少個蘑菇,但是第\(k\)秒你必須在終點位置。哦對了,如果這場遊戲你不能吃夠\(t\)個蘑菇或者第\(k\)秒你走不到終點,你就不是一個合格的隊友,就不輸出你吃的蘑菇數,輸出\(hashaki\)

Input
第一行四個整數,分別表示\(n,m, t, k\)

接下來一個\(n\)\(m\)列的矩陣\(mp\)

,意義如題。

\(1\leq n,m\leq 10,1\leq t,k\leq 1000,mp[i][j]\leq 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,那麼一般會存在狀態\(dp[n][m]\),現在由於有時間的限制,所以應該要列舉時間這個維度,而且時間資料也不大,那麼就暗示著可以加在\(dp\)狀態中,於是就有了\(dp[n][m][t]\)。然後你還得考慮每次是從哪個方向轉移過來的,也就是一定會有方向的列舉,那麼就有了\(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);

但需要注意的是每次列舉的上一個方向必須是之前走過的,所以我們要對\(dp\)進行一下預處理,最後對4個方向上的\(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;
}