【BZOJ2595】【WC2008】游览计划

【题目链接】

【思路要点】

  • 可以直接用基于连通性的的动态规划求解,或者可以用斯坦纳树DP+SPFA求解。
  • 时间复杂度\(O(N*M*3^K)\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
#define MAXN	15
#define MAXQ	2000005
#define CURR	1024
#define INF	1e9
struct pos {int x, y, curr; };
int ans[MAXN][MAXN][CURR];
pos path[MAXN][MAXN][CURR];
int n, m, k, value[MAXN][MAXN], num[MAXN][MAXN];
bool inq[MAXN][MAXN][CURR], mark[MAXN][MAXN];
void work(pos home) {
	mark[home.x][home.y] = true;
	if (path[home.x][home.y][home.curr].curr == 0) return;
	pos tmp = path[home.x][home.y][home.curr];
	if (home.curr == tmp.curr) work(tmp);
	else work(tmp), work((pos) {tmp.x, tmp.y, home.curr ^ tmp.curr});
}
void update(int pi, int pj, int pc, int ti, int tj, int tc, int delta) {
	int tmp = ans[ti][tj][tc] + delta;
	if (tmp < ans[pi][pj][pc]) {
		ans[pi][pj][pc] = tmp;
		path[pi][pj][pc] = (pos) {ti, tj, tc};
	}
}
void push(pos a) {
	inq[a.x][a.y][a.curr] = true;
}
void pop(pos a) {
	inq[a.x][a.y][a.curr] = false;
}
bool query(pos a) {
	return inq[a.x][a.y][a.curr];
}
int main() {
	cin >> n >> m; k = 0;
	for (int i = 1; i <= n; i++)
	for (int j = 1; j <= m; j++) {
		cin >> value[i][j];
		if (value[i][j] == 0) num[i][j] = k++;
	}
	for (int i = 1; i <= n; i++)
	for (int j = 1; j <= m; j++)
	for (int curr = 1; curr < 1 << k; curr++)
		if (value[i][j] == 0 && curr == 1 << num[i][j]) ans[i][j][curr] = 0;
		else ans[i][j][curr] = INF;
	for (int curr = 1; curr < 1 << k; curr++) {
		static pos q[MAXQ];
		int l = 0, r = -1;
		for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			for (int l = (curr - 1) & curr; l; l = (l - 1) & curr)
				update(i, j, curr, i, j, l, ans[i][j][curr ^ l] - value[i][j]);
			if (ans[i][j][curr] < INF) {
				q[++r] = (pos) {i, j, curr};
				push(q[r]);
			}
		}
		while (l <= r) {
			pos tmp = q[l++];
			pop(tmp);
			if (ans[tmp.x][tmp.y][tmp.curr] + value[tmp.x][tmp.y + 1] < ans[tmp.x][tmp.y + 1][tmp.curr]) {
				ans[tmp.x][tmp.y + 1][tmp.curr] = ans[tmp.x][tmp.y][tmp.curr] + value[tmp.x][tmp.y + 1];
				path[tmp.x][tmp.y + 1][tmp.curr] = tmp;
				if (!query((pos) {tmp.x, tmp.y + 1, tmp.curr})) {
					q[++r] = (pos) {tmp.x, tmp.y + 1, tmp.curr};
					push(q[r]);
				}
			}
			if (ans[tmp.x][tmp.y][tmp.curr] + value[tmp.x][tmp.y - 1] < ans[tmp.x][tmp.y - 1][tmp.curr]) {
				ans[tmp.x][tmp.y - 1][tmp.curr] = ans[tmp.x][tmp.y][tmp.curr] + value[tmp.x][tmp.y - 1];
				path[tmp.x][tmp.y - 1][tmp.curr] = tmp;
				if (!query((pos) {tmp.x, tmp.y - 1, tmp.curr})) {
					q[++r] = (pos) {tmp.x, tmp.y - 1, tmp.curr};
					push(q[r]);
				}
			}
			if (ans[tmp.x][tmp.y][tmp.curr] + value[tmp.x + 1][tmp.y] < ans[tmp.x + 1][tmp.y][tmp.curr]) {
				ans[tmp.x + 1][tmp.y][tmp.curr] = ans[tmp.x][tmp.y][tmp.curr] + value[tmp.x + 1][tmp.y];
				path[tmp.x + 1][tmp.y][tmp.curr] = tmp;
				if (!query((pos) {tmp.x + 1, tmp.y, tmp.curr})) {
					q[++r] = (pos) {tmp.x + 1, tmp.y, tmp.curr};
					push(q[r]);
				}
			}
			if (ans[tmp.x][tmp.y][tmp.curr] + value[tmp.x - 1][tmp.y] < ans[tmp.x - 1][tmp.y][tmp.curr]) {
				ans[tmp.x - 1][tmp.y][tmp.curr] = ans[tmp.x][tmp.y][tmp.curr] + value[tmp.x - 1][tmp.y];
				path[tmp.x - 1][tmp.y][tmp.curr] = tmp;
				if (!query((pos) {tmp.x - 1, tmp.y, tmp.curr})) {
					q[++r] = (pos) {tmp.x - 1, tmp.y, tmp.curr};
					push(q[r]);
				}
			}
		}
	}
	int finalans = INF, goal = (1 << k) - 1;
	pos home;
	for (int i = 1; i <= n; i++)
	for (int j = 1; j <= m; j++)
		if (ans[i][j][goal] < finalans) {
			finalans = ans[i][j][goal];
			home = (pos) {i, j, goal};
		}
	cout << finalans << endl;
	work(home);
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++)
			if (mark[i][j]) {
				if (value[i][j]) cout << 'o';
				else cout << 'x';
			} else cout << '_';
		cout << endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值