捡金币

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;
}


### 实现角色拾取金币功能的完整方案 在 Unity 中实现角色拾取金币的功能可以通过以下方式完成。以下是详细的说明: #### 场景搭建与基础配置 为了实现“吃金币”的功能,首先需要构建基本的游戏场景。这包括创建一个球体作为家控制的角色,并设置金币对象供角色拾取。 - **摄像机设置** 调整摄像机的位置角度以获得更好的视野。例如,将摄像机位置设为 `(0, 10, -10)` 并沿 X 轴旋转 90 度,使摄像机俯视整个游戏区域[^1]。 - **角色与金币模型** 创建一个球体作为可移动的角色,并为其添加 `Rigidbody` 组件,同时勾选 `Is Kinematic` 属性以允许手动控制物体运动。接着,在场景中放置一个立方体代表金币,并调整其颜色或材质(如金色)以便于识别。 #### 编写脚本逻辑 接下来,通过编写脚本来定义金币被拾取后的行为以及更新分数机制。 - **碰撞检测脚本 (`Coin.cs`)** 使用触发器事件来检测当角色进入金币范围时发生的交互动作。下面是一个简单的示例代码片段用于处理此类情况: ```csharp using System.Collections; using System.Collections.Generic; using UnityEngine; public class Coin : MonoBehaviour { private Animator ani_coin; void Start() { ani_coin = GetComponent<Animator>(); } private void OnTriggerEnter2D(Collider2D collision) { if (collision.CompareTag("Player")) { // 假定家标签名为 "Player" Destroy(gameObject); // 销毁当前金币实例 ScoreManager.AddScore(10); // 更新全局得分管理器中的分数 SoundManager.PlaySound("CollectCoin"); // 可选:播放声音效果 } } } ``` 此段代码实现了当带有特定标记 ("Player") 的实体触碰到该金币时销毁自己并调用外部函数增加相应分值的操作][^[^23]。 - **得分管理系统 (`ScoreManager.cs`)** 需要单独维护一个类用来跟踪总积分变化状况。这里给出一段可能实现的例子: ```csharp using UnityEngine; using UnityEngine.UI; public static class ScoreManager { private static int scoreValue = 0; public static Text scoreText; // UI上的文本显示控件 public static void AddScore(int value){ scoreValue +=value ; if(scoreText!=null ){ scoreText.text="Score:"+scoreValue.ToString(); } } } ``` 上述静态方法每次都会累加传入参数指定数量至内部变量`socreValue`, 同时刷新界面上对应的数值展示[^5]。 #### 用户界面(UI)设计 最后一步是确保有适当的方式让家看到他们的进展——即实时更新的成绩栏。为此需引入Unity内置工具包Canvas及其子元素Text组件形成直观易懂的表现形式。 --- ### 注意事项 虽然以上提供了完整的解决方案框架,但在实际开发过程中还需考虑更多细节优化用户体验,比如动画过渡效果、粒子特效渲染等附加特性提升视觉冲击力;此外也要兼顾性能消耗平衡问题以免影响整体流畅度表现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值