20191114 csp-s模拟T1(最短路+全排列)

T1 金币(coin)

(WOJ4819)
【题目描述】
沙雕在玩一款收集类游戏,这款游戏每一关的地图可以看成一个 n n n n n n 列的网格,每个格子有宝藏( T T T)、起点( S S S)、红色( R R R)、绿色( G G G)、蓝色( B B B)五种可能,其中起点有且仅有一个,宝藏有且仅有 k k k 个。沙雕从起点出发寻宝,每一步可以向上下左右四个方向中的一个行走一步,但不能走出网格;它需要从起点出发,在关卡中收集至少 L X LX LX 个、至多 R X RX RX 个宝藏,收集完毕后可以立即过关(无论停留在哪个网格)。当沙雕每落在一个红色格子时,它需要付出 R R R 个金币;每落在一个绿色格子时,它需要付出 G G G个金币;每落在一个蓝色格子时,它需要付出 B B B 个金币;落在一个宝藏格子时,它可以收集宝藏并获得 T T T 个金币,然后这个宝藏格子将变成空地。
沙雕希望自己在过关的时候获得尽量多的金币(若必须付出若干金币则认为获得负数的金币),你能帮它计算出它最多获得多少金币吗?
【输入格式】
从文件coin.in 中读入数据。
第一行为五个非负整数 n 、 k 、 T 、 L X 、 R X n、k、T、LX、RX nkTLXRX
第二行为三个非负整数 R 、 G 、 B R、G、B RGB
接下来n 行每行为一个长度为n 的字符串,表示网格地图。
【输出格式】
输出到文件coin.out 中。
输出一行一个整数表示最多能获得的金币数。
【样例1 输入】
5 5 100 2 3
10 2 5
RGRTB
TSGGB
RRBTG
BTRBG
RBRTR
【样例1 输出】
294
【样例1 解释】
一个可行的最优解为:左右右右上下下。
【样例2】
见选手目录下的coin/coin2.in 与coin/coin2.ans。
【子任务】
对于100% 的数据满足: 1 ≤ n ≤ 501 , 1 ≤ L X ≤ R X ≤ k ≤ 10 , 0 ≤ R , G , B , T ≤ 1 0 5 1\le n\le 501,1\le LX\le RX \le k\le 10,0\le R,G,B,T\le 10^5 1n501,1LXRXk10,0R,G,B,T105

思路:
以每个宝藏为起点跑最短路,求出每个宝藏到另外宝藏的最小花费。全排列宝藏,计算最大收入即可。
考试不要想复杂了!

代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define re register

inline int in{
	int s=0,f=1;char x;
	for(x=getchar();!isdigit(x);x=getchar())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=getchar())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

const int A=505;
const int N=3e5+5;
const int INF=1e9;
int n,s_all,lx,rx;
int T,R,G,B;
char ch;
int mapp[A][A];
int s_x[15],s_y[15],s_k;
int star[15];
int val[N];
int head[N],tot;
struct Road{
	int nex,to;
}road[8*N];
inline void ljb(int x,int y){
	road[++tot].nex=head[x],road[tot].to=y,head[x]=tot;
}
int head_[100],tot_;
struct Road_{
	int nex,to,w;
}road_[2000];
inline void ljb_(int x,int y,int w){
	road_[++tot_].nex=head_[x],road_[tot_].to=y,road_[tot_].w=w,head_[x]=tot_;
}
inline int d(int x,int y){
	return (x-1)*n+y;
}
int s[15][N];
bool ex[N];
int s_[15][15];
bool use[15];
int ans=-INF;

inline void scan(){
	n=in,s_all=in,T=in,lx=in,rx=in,R=in,G=in,B=in;
	for(re int i=1;i<=n;++i)
		for(re int j=1;j<=n;++j){
			ch=getchar();
			while(ch!='T'&&ch!='S'&&ch!='R'&&ch!='G'&&ch!='B')
				ch=getchar();
			if(ch=='T'){
				mapp[i][j]=T;
				s_x[++s_k]=i,s_y[s_k]=j;
			}  
			if(ch=='S'){
				mapp[i][j]=0;
				s_x[0]=i,s_y[0]=j;
			}
			if(ch=='R')  mapp[i][j]=(-1)*R;
			if(ch=='G')  mapp[i][j]=(-1)*G;
			if(ch=='B')  mapp[i][j]=(-1)*B;
		}
	return;
}

inline void build(){
	for(re int i=0;i<=s_all;++i)
		star[i]=d(s_x[i],s_y[i]);
	for(re int i=1;i<=n;++i)
		for(re int j=1;j<=n;++j)
			if(mapp[i][j]<0)	val[d(i,j)]=(-1)*mapp[i][j];
	for(re int i=1;i<=n;++i)
		for(re int j=1;j<=n;++j){
			if(i-1>0&&i-1<=n)   ljb(d(i,j),d(i-1,j));
			if(i+1>0&&i+1<=n)   ljb(d(i,j),d(i+1,j));
			if(j-1>0&&j-1<=n)   ljb(d(i,j),d(i,j-1));
			if(j+1>0&&j+1<=n)   ljb(d(i,j),d(i,j+1));
		}
	return;
}

inline void djk(int now){
	fill(s[now]+1,s[now]+d(n,n)+1,INF);
	memset(ex,0,sizeof(ex));
	priority_queue <pair<int,int> > q;
	int be=star[now];
	s[now][be]=0,ex[be]=0;
	q.push(make_pair(0,be));
	while(!q.empty()){
		int x=q.top().second;q.pop();
		if(ex[x])   continue;
		ex[x]=1;
		for(re int y=head[x];y;y=road[y].nex){
			int z=road[y].to;
			if(s[now][z]>s[now][x]+val[z]){
				s[now][z]=s[now][x]+val[z];
				q.push(make_pair(-s[now][z],z));
			}
		}
	}
	return;
}

inline void prepare(){
	for(int i=0;i<=s_all;i++)
		for(int j=0;j<=s_all;j++)
			s_[i][j]=s[i][star[j]];
	return;
}

inline void DFS(int now,int num,int mon){
	if(num>rx)  return;
	if(num>=lx&&num<=rx)
		ans=max(ans,mon);
	if(ans+(s_all-num)*T<=ans)  return;
	for(int i=0;i<=s_all;i++){
		if(use[i])  continue;
		use[i]=1;
		DFS(i,num+1,mon-s_[now][i]+T);
		use[i]=0;
	}
	return;
}

signed main(){
	scan();
	build();
	for(re int i=0;i<=s_all;++i)
		djk(i);
	prepare();
	use[0]=1;
	DFS(0,0,0);
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值