捡金币

3.捡金币
(coin.cpp/c/pas)
【问题描述】
小空正在玩一个叫做捡金币的游戏。游戏在一个被划分成n行n列的网格状场地中进行。
每一个格子中都放着若干金币,并且金币的数量会随着时间而不断变化。小空的任务就是在
网格中移动,拾取尽量多的金币。并且,小空还有一个特殊技能“闪现”,能帮助她在网格间
快速移动。
捡金币游戏的具体规则如下:在每一秒开始时,每个网格内都会出现一定数量的金币,
而之前在这格没有被拾取的金币就消失了。在游戏开始时,也就是第 1 秒的开始,小空可以
选择任意一个网格作为起点开始本次游戏,并拾取起点内的金币。之后,在下一秒到来前,
小空可以选择走路移动到与她所在的格子上、下、左、右相邻的一个格子中,或者呆在原地
不动,并在下一秒开始时拾取到她所在的格子中的金币。或者,小空可以选择使用闪现技能,
使用一次闪现时,她先选择上、下、左、右一个方向,之后向该方向移动两格。小空可以在
一秒内使用多次闪现,但不能超过 C 次。在一秒内使用的多次闪现必须向同一个方向移动,
若使用 x 次闪现,便可以向一个方向移动正好 2x 格,并且她也只能在下一秒开始时收集到
连续闪现结束后所在的那一格中的金币。如果在某一秒钟小空使用了闪现,那么她就不能选
择通过走路移动了,反过来也是如此。无论走路或者使用闪现,小空都不能移动到整个场地
之外。整个游戏共进行 T 秒,在第 T 秒开始时,小空将会拾取她所在的格子中的金币,并结
束游戏。小空在整局游戏内一共只能使用最多 W 次闪现。
举个例子,在如下 3*3 的场地中,游戏共进行 3 秒,下表列出了 3 秒开始时每一格内的
金币数量。
如果小空选择在第 1 行第 1 列开始游戏,那么在第 1 秒开始时她会获得 1 枚金币。接下
来,如果她选择向右走,那么在第 2 秒开始时她会出现在第 1 行第 2 列并获得 3 枚金币。接
下来,过她选择向下进行 1 次闪现,那么在第 3 秒开始时她会出现在第 3 行第 2 列并获得 2
枚金币,游戏结束,一共获得 6 枚金币。
又如,在如下 5*5 的场地中(只列出了第 1 行所含金币数),游戏共进行 2 秒,如果小
空选择在第 1 行第 1 列开始游戏,则她会获得 1 枚硬币,之后若向右连续闪现 2 次,那么在
第 2 秒开始时她会出现在第 1 行第 5 列,并获得 2 枚硬币,总共获得 3 枚硬币。
现在,给出游戏场地的大小 n,每秒钟开始时各个位置会出现的金币数,小空一秒内最
多使用闪现的次数 C,小空在整局游戏中使用闪现的最多次数 W,整局游戏的总时间 T,请
你告诉小空她最多可以获得多少枚金币。
【输入】
输入的第 1 行包含 4 个整数 n,C,W,T,意义如问题描述中所述。
接下来包含 n 个 n*n 的矩阵,第 k 个矩阵的第 i 行第 j 列表示第 i 行第 j 列的格子在第 k
秒开始时出现的金币数(记作s i,j,k )。相邻两个矩阵间用一个空行隔开。
【输出】
输出包含一个整数,表示游戏结束时小空最多可以获得的金币数量。
【输入输出样例 1】
coin.in  coin.out
3 1 1 3
1 3 4
3 2 1
1 3 2
2 3 1
1 3 2
2 1 4
3 3 1
3 2 1
2 3 1
11
见选手目录下的 coin / coin1.in 与 coin / coin1.out
【输入输出样例 1 说明】
选择在第 1 行第 3 列开始游戏,获得 4 枚金币;在第 2 秒开始时向下闪现到第 3 行第 3
列,获得 4 枚金币;在第 3 秒开始时向左走到第 3 行第 2 列,获得 3 枚金币,游戏结束。一
共获得 11 枚金币。
【输入输出样例 2】
见选手目录下的 coin / coin2.in 与 coin / coin2.out
【数据规模与约定】
测试点编号  n  C  W  T  s i,j,k
1
≤5  ≤2  ≤4  ≤5
≤1,000
2
3
4
≤21  ≤10  ≤80  ≤80  5
6
7
≤25
=100
≤150  ≤100
8
9
≤12
10

对 100%的数据,n≥1,C≥0,W≥0,T≥1,s i,j,k ≥0




分析:

根据题意可以很直白的得出状态f[T][x][y][k]表示T秒,(x,y),总共用了k次闪现。加个滚动数组优化一维。因为走的转移为O(1),而闪现的转移:

f[T][x][y][k]=max(f[T][nx][ny][k-use]),(nx,ny)为闪现use次后的坐标,需要O(C)的时间,转移,这是我们优化的重点,我们必须将其优化为O(1)才能过,怎么办?O(1)->单调队列维护转移。

我们画一个图,以向左闪现为例:

f[T][x][y][k]=max(f[T][x][y+2][k-1],f[T][x][y+2*2][k-1*2],f[T][x][y+2*3][k-1*3].....)

我们可以维护一个单调队列q[head]=max(f[T][x][y+2][k-1]....),并转移到f[T][x][y][k]后,将其加入队列,以便维护下一个f[T][x][y-2][k+1]以O(1)转移。


参考程序:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=160;
int a[maxn][maxn],vis[maxn][maxn];
int f[2][160][110][110];
int direction[4][2]={
	{1,0},{0,1},{-1,0},{0,-1},
};
int n,C,W,T,res=0,cnt=0,tag=0,head,tail;
int q[maxn*maxn],qc[maxn*maxn];
const int inf=1e9+7;
bool ok(int x,int y){
	if (0<x && x<=n && 0<y && y<=n)return true;
	return false;
}
void clear(){
	cnt=0;head=1;tail=0;
	q[1]=-inf;
}
void push(int x){
	int sum=1;
	while (head<=tail && x>=q[tail])sum+=qc[tail--];
	q[++tail]=x;qc[tail]=sum;
	if (++cnt>C)if (--qc[head]==0)head++;
}
int main(){
	freopen("coin.in","r",stdin);
	freopen("coin.out","w",stdout);
	scanf("%d%d%d%d",&n,&C,&W,&T);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			scanf("%d",&f[0][0][i][j]);
	for (int K=1;K<T;K++){
		int p=K&1;int t=1-p;
		for (int x=1;x<=n;x++)
			for (int y=1;y<=n;y++)
				scanf("%d",&a[x][y]);
		for (int x=1;x<=n;x++){
			++tag;
			for (int y=1;y<=n;y++)
				for (int j=0;j<=W;j++)
					if (vis[y][j]!=tag){
						clear();
						int yy=y,jj=j;
						while (yy<=n && jj<=W){
							vis[yy][jj]=tag;
						    f[p][jj][x][yy]=max(f[p][jj][x][yy],q[head]);
							push(f[t][jj][x][yy]);
							yy+=2;
							jj++;
						}
					}
			++tag;
			for (int y=n;y>0;y--)
				for (int j=0;j<=W;j++)
					if (vis[y][j]!=tag){
						clear();
						int yy=y,jj=j;
						while (yy>0 && jj<=W){
							vis[yy][jj]=tag;
							f[p][jj][x][yy]=max(f[p][jj][x][yy],q[head]);
							push(f[t][jj][x][yy]);
							yy-=2;
							jj++;
						}
					}
		}
		for (int y=1;y<=n;y++){
			++tag;
			for (int x=1;x<=n;x++)
				for (int j=0;j<=W;j++)
					if (vis[x][j]!=tag){
						clear();
						int xx=x,jj=j;
						while (xx<=n && jj<=W){
							vis[xx][jj]=tag;
							f[p][jj][xx][y]=max(f[p][jj][xx][y],q[head]);
							push(f[t][jj][xx][y]);
							xx+=2;
							jj++;
						}
				    }
			++tag;
			for (int x=n;x>0;x--)
				for (int j=0;j<=W;j++)
					if (vis[x][j]!=tag){
						clear();
						int xx=x,jj=j;
						while (xx>0 && jj<=W){
							vis[xx][jj]=tag;
							f[p][jj][xx][y]=max(f[p][jj][xx][y],q[head]);
							push(f[t][jj][xx][y]);
							xx-=2;
							jj++;
						}
					}
		}
		for (int x=1;x<=n;x++)
			for (int y=1;y<=n;y++)
				for (int j=0;j<=W;j++){
					f[p][j][x][y]=max(f[p][j][x][y],f[t][j][x][y]);
					for (int k=0;k<5;k++){
						int nx=x+direction[k][0];
						int ny=y+direction[k][1];
						if (ok(nx,ny))f[p][j][x][y]=max(f[p][j][x][y],f[t][j][nx][ny]);
					}
				}
		for (int x=1;x<=n;x++)
			for (int y=1;y<=n;y++)
				for (int j=0;j<=W;j++)
					f[p][j][x][y]+=a[x][y];
	}
	int p=(T-1)&1;
	for (int x=1;x<=n;x++)
		for (int y=1;y<=n;y++)
			for (int j=0;j<=W;j++)
			    res=max(res,f[p][j][x][y]);
	printf("%d",res);
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 为了实现这个游戏,你可以使用 C# WinForms 创建一个用户界面,并使用控件如按钮、文本框、图片框等来构建游戏的基本界面。 首先,你需要创建一个主窗体,在上面放置游戏区域的图片框。然后,可以使用按钮控件来控制游戏人物的移动。你可以使用两个按钮,一个用于向左移动,一个用于向右移动。 接下来,你可以使用图像控件来显示游戏人物和敌人。在游戏循环中,你可以使用定时器控件来控制敌人的移动。当游戏人物与敌人碰撞时,可以使用文本框来显示游戏结束的信息。 对于商店系统,你可以使用多个图像控件来显示不同的物品,并使用文本框来显示物品的价格。当玩家购买物品时,可以使用文本框来显示剩余的金币数量。 除此之外,你还可以使用一些其他控件,如进度条、单选按钮等,来增强游戏的功能和用户体验。 ### 回答2: C# Winform可以很方便地编写一个可以走来走去金币的游戏,下面简要介绍一下如何实现。 首先,我们需要设计游戏场景,可以使用PictureBox控件作为玩家和敌人的图像载体,以及Label控件显示当前金币数量和分数等信息。 其次,为了使游戏难度提高,可以在游戏场景中添加至少20种敌人,每种敌人具有不同的行动规则和速度。可以创建一个Enemy类,并在游戏场景中实例化各种敌人对象,根据定时器控件的Tick事件,控制敌人的移动和碰撞检测。 接下来,我们可以在游戏场景中随机生成金币,每次玩家接触金币就会增加分数和金币数量。可以创建一个Coin类,并使用Graphics类绘制金币的图像,以及使用碰撞检测判断玩家是否接触到金币。 为了增加游戏的乐趣和挑战,可以设计一些障碍物,例如墙壁、水域、陷阱等,这些障碍物可以阻碍玩家的行动,增加游戏的难度。 最后,添加商店系统可以提供一些道具和武器等供玩家购买,道具可以提供一些增益效果,而武器可以增强玩家的攻击能力。可以创建一个Shop类,在商店中展示道具和武器的信息,并使用按钮控件作为购买操作的触发事件。 总之,用C# Winform编写一个可以走来走去金币、高难度、有敌人、商店系统的马里奥类小游戏十分有趣,通过合理的设计和编码,可以实现一个令人兴奋的游戏体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值