Codeforces 375C BFS+射线,闭合曲线内挖宝防挖地雷

22 篇文章 0 订阅

题目链接:http://codeforces.com/contest/375/problem/C

题意:

.是空地 #是墙, 12-8(连续)是宝藏,每个宝藏价值是下面对应位置的数字
从起点出发,走出一个闭合曲线回到终点 , 获得闭合曲线内的所有宝藏
闭合曲线内不能有 B(地雷)
S是起点+终点
雷和宝藏个数 不超过8个
走一步要扣一块钱,问最后能赚到多少钱
思路:
BFS + 射线
 
判断宝藏是否在闭合曲线内,可以判断经过了宝藏下方的次数(只需要判断经过下方次数即可,因为起点终点相同,所以经过上方次数与下方次数要么相同要么±2
关键的一点:奇偶数相同)
所以我们可以得到一个结论: 对于某点是否在闭合曲线内,我们可以判断 曲线穿过该点下方的次数(奇数次就是 在曲线内 )
 
 
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
#define N 21
struct node{
	int x, y, st;
	node(int a=0,int b=0,int c=0):x(a),y(b),st(c){}
}S;
char mp[N][N];
int V[8][2], cnt;//宝藏和地雷坐标 cnt 宝藏和地雷数
int number[8], value[8];//number[cnt] >=0为宝藏编号 -1为地雷
int f[N][N][1<<8], step[4][2] = {1,0, -1, 0, 0,1, 0,-1};
int n, m;
bool valid(int x,int y){return 0<=x && x<n && 0<=y && y<m &&( mp[x][y] == '.' || mp[x][y] == 'S');}
int get(int x,int y,int nowy,int ST){
	for(int i = 0; i < cnt; i++)
		if(x>V[i][0] &&	((y == V[i][1] && nowy == V[i][1]+1)||(nowy == V[i][1] && y ==  V[i][1]+1)))
			ST ^= 1<<i;
	return ST;
}
int BFS(){
	memset(f, -1, sizeof(f));
	queue<node>q; q.push(S);
	f[S.x][S.y][0] = 0;
	while(!q.empty())
	{
		node u = q.front(); q.pop();
		for(int i = 0; i < 4; i++)
		{
			node v = u; 	v.x += step[i][0], v.y += step[i][1];
			if(!valid(v.x, v.y))continue;
			v.st = get(v.x, v.y, u.y, u.st);
			if(f[v.x][v.y][v.st] != -1)continue;
			f[v.x][v.y][v.st] = f[u.x][u.y][u.st] +1;
			q.push(v);
		}
	}
	int ans = 0;
	for(int i = 0;i<(1<<cnt);i++)if(f[S.x][S.y][i]!=-1){
		bool bomb = false;
		int sum = 0;
		for(int j = 0; j < cnt; j++)if(i & (1<<j)){
			if(number[j] == -1){bomb = true; break;}
			sum += value[number[j]];
		}
		if(bomb)continue;
		ans = max(ans, sum - f[S.x][S.y][i]);
	}
	return ans;
}
void init(){
	cnt = 0;
	int baozhang = 0;
	memset(number, -1, sizeof(number));
	for(int i = 0; i < n; i ++){
		scanf("%s",mp[i]);
		for(int j = 0; j < m; j++)	if(mp[i][j]=='B')
		{
			V[cnt][0] = i; V[cnt++][1] = j;
		} else if('1'<=mp[i][j] && mp[i][j] <='8')
		{
			V[cnt][0] = i; V[cnt][1] = j;
			number[cnt++] = mp[i][j] - '1';
			baozhang++;
		}
		else if(mp[i][j] == 'S')
		{
			S.x = i, S.y = j;	S.st = 0;
		}
	}
	for(int i = 0; i < baozhang; i++)scanf("%d",&value[i]);
}
int main() {
	int i, j;
	while(~scanf("%d %d",&n,&m)){
		init();
		printf("%d\n", BFS());
	}
	return 0;
}
/*
4 4
....
.S1.
....
....
10
7 7
.......
.1###2.
.#...#.
.#.B.#.
.3...4.
..##...
......S
100
100
100
100
7 8
........
........
....1B..
.S......
....2...
3.......
........
100
-100
100
1 1
S
10 11
........S..
...........
5........1.
.7#........
...64.#....
...........
..........#
.2.........
.....3.....
...........
-9
33
9
-20
12
10
-29

*/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值