寻找宝藏【SGOI-14】

5 篇文章 0 订阅
3 篇文章 0 订阅

题目描述

【背景】
据说在意大利的米兰市的地下,埋藏着一堆的宝藏。一天,一个名叫 Shevchenko 的人来到这地下宝库,准备把所有的宝藏都搬回家。当他来到这里时,发现这里是一个迷宫,宝藏埋藏在各个角落,在另一端有个出口,当他每捡到一个宝藏将恢复一定的体力值。
【任务】
由于 Sheva(Shevchenko 的昵称)的体力有限,他必须合理的安排体力,才能既捡完所有的宝藏又能离开这个宝库,现在请你编一程序,帮 Sheva 解决这个问题。在迷宫中每走一格,Sheva 的体力值都会减少一定的数量,如果体力值小等于 0,他将不能再前行。

输入输出格式

输入格式:

第一行输入 Sheva 刚开始的体力值和每走一格消耗的体力值(均为整数)。
第二行输入宝藏的个数 N(N<=10)。
第三行到第 N+2 行每行有3 个数,前两个数为宝藏的坐标 X、Y,第 3 个数为捡到这宝藏将获得的体力值。
下一行输入迷宫的行数 A和列数 B。(A、B<=50)
以下 A行将输入迷宫的地形。0 表示不能走,1 表示可以走(宝藏所在的位置也为 1)。
最后一行输入出口的坐标(起点坐标为(1,1))。

输出格式:

如果 Sheva 能走出迷宫,将只输出一个数,为他所剩的体力值。否则,将输出“no,he cannot”(注意大小写)。

输入输出样例

输入样例#1:

100 1 

1 4 2 
2 4 2 
4 4 2 
5 3 2 
5 5 
1 1 0 1 0 
0 1 1 1 0 
0 1 0 0 1 
0 1 1 1 0 
0 1 1 1 1 
5 5 

输出样例#1:

92

正文

SCOI-14的其他题目都与数论有或多或少的关系,该题目应当是其中较为简单的一道。

题目简意

在图中(1表示可走,0表示不可走)有N个坐标,走到坐标i可增加Ai体力值,另外,每走一步消耗tl体力值,求到终点时最多还剩多少体力值。

算法分析

看数据范围:
 

(N<=10)(A、B<=50)

数据量极小,边权为1,可采用bfs预处理出起点,终点及宝藏之间的最短路径。

时间复杂度:O (N*A*B)

求得后,暴力枚举所有可能的路径,以dfs实现。

时间复杂度:O(N!)

极限数据(N=10,A=B=50)下,算法时间复杂度为O(10!+10*50*50)\approxO(4000000)

#include<bits/stdc++.h>
using namespace std;
int TL,tl,N,n,m,d[20][20],br[60][60],maxn=-0x7fffff,had[20];
bool _map[60][60];
bool vis[60][60];
int dx[5]={0,-1,1,0,0};
int dy[5]={0,0,0,-1,1};
struct B{
	int x;
	int y;
	int add_tl;
}ed,bz[13];
struct S{
	int x;
	int y;
	int step;
};
queue<S> q;
void dfs(int s,int val,int k)
{
	if(k==N)
	{
		maxn=max(maxn,val-d[s][N+1]);
		return;
	}
	for(int i=1;i<=N;i++)
	{
		if(!had[i])
		{
			had[i]=1;
			dfs(i,val-d[s][i]+bz[i].add_tl,k+1);
			had[i]=0;
		}
	}
	return;
}
int main()
{
	cin>>TL>>tl;
	cin>>N;
	memset(br,-1,sizeof(br));
	memset(d,0x3f,sizeof(d));
	for(int i=1;i<=N;i++) 
	{
		cin>>bz[i].x>>bz[i].y>>bz[i].add_tl;
		br[bz[i].x][bz[i].y]=i;
	}
	bz[0].x=bz[0].y=1;
	cin>>n>>m;
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>_map[i][j];
	cin>>ed.x>>ed.y;
	bz[N+1].x=ed.x;
	bz[N+1].y=ed.y;
	br[1][1]=0;
	br[ed.x][ed.y]=N+1;
	for(int i=0;i<=N+1;i++)
	{
		q.push((S){bz[i].x,bz[i].y,0});
		memset(vis,0,sizeof(vis));
		vis[bz[i].x][bz[i].y]=1;
//		cout<<i<<":\n";
		while(!q.empty())
		{
			S tmp=q.front();
			q.pop();
			int ux=tmp.x,uy=tmp.y,us=tmp.step;
//			cout<<ux<<' '<<uy<<' '<<us<<endl;
			for(int j=1;j<=4;j++)
			{
				int xx=ux+dx[j];
				int yy=uy+dy[j];
				if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&_map[xx][yy]&&!vis[xx][yy])
				{
					if(br[xx][yy]!=-1) 
					{
						d[i][br[xx][yy]]=d[br[xx][yy]][i]=(us+1)*tl;
//						printf("add_edge(%d,%d,%d)\n",i,br[xx][yy],(us+1)*tl);
					}
					q.push((S){xx,yy,us+1});
					vis[xx][yy]=1;
				}
			}
		}
	}
	br[1][1]=br[ed.x][ed.y]=-1;
	dfs(0,TL,0);
	for(int i=0;i<=N+1;i++)
	{
		for(int j=0;j<=N+1;j++)
		{
			if(i!=j&&d[i][j]==d[19][19])
			{
				cout<<"no,he cannot";
				return 0;
			}
		}
	}
	if(maxn>=0) cout<<maxn;
	
	else cout<<"no,he cannot";
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值