• 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),意义如题。

    (1leq n,mleq 10,1leq t,kleq 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
    ");
    	else printf ("%d
    ",ans);
    	return 0;
    }
    
    路漫漫兮
  • 相关阅读:
    代理模式
    面向对象设计原则
    砝码破碎
    阿里EasyExcel使用
    IBM的OpenJ9
    java反射 (复习)
    DecimalFormat保留小数
    Object类
    SQLMAP用法
    SQL盲注之时间注入
  • 原文地址:https://www.cnblogs.com/lonely-wind-/p/13396928.html
Copyright © 2020-2023  润新知