HDU - 4568 最短路 + 状压dp

题意:

给出一个n*m的方格矩阵。其中某些点有宝藏,每个方格都有一个经过的代价,若为-1则不能经过,现在一个猎人需要将所有宝藏都拿走,他可以从矩阵边界任意的位置进入,也可以从边界上任意位置离开,(但是要注意只能进入和离开各一次),求把所有宝藏拿走的最小代价,如果不能拿走则输出-1。

思路:

这道题题意很坑,经过实践发现两个坑:
1.猎人只能进入矩阵和离开矩阵各一次,否则第二个样例答案就是10了,先取(1,1)位置然后离开,然后再取(2,2)位置。
2.从题意上看都不明白到底是只要有一个宝藏拿不走就输出-1,还是所有宝藏都拿不走才输出-1,然而想这个并没有什么luan用,因为根本不存在输出-1的情况,代码里面没考虑-1都过了。

除去这些坑点,来思考这道题,注意到k非常小,这样很显然需要状压,那么先求k个宝藏到其他任意点的最短路,然后对这k个点进行dp,dp的方式类似于旅行商问题,设dp[S][u]表示宝藏已经拿到的状态为S,并且当前处于第u个宝藏的位置,那么状态转移方程就是dp[S][u] = min(dp[S][u], dp[S ^ (1 << u)][v] + d[v][u],计算的时候要保证注意u一定属于S,且v也一定属于S^(1<

代码:

#include 
#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 205;
const int INF = 0x3f3f3f3f;

struct node {
	int x, y;
};

int n, m, k;
bool vis[MAXN][MAXN];
int mp[MAXN][MAXN], d[15][MAXN][MAXN];
const int dx[] = {0, 0, -1, 1};
const int dy[] = {-1, 1, 0, 0};

void spfa(node s, int id) {
	queue  q;
	memset(vis, false, sizeof(vis));
	memset(d[id], INF, sizeof(d[id]));
	vis[s.x][s.y] = true; d[id][s.x][s.y] = 0;
	q.push(s);
	while (!q.empty()) {
		node u = q.front(); q.pop();
		vis[u.x][u.y] = false;
		for (int i = 0; i < 4; i++) {
			int nx = u.x + dx[i], ny = u.y + dy[i];
			if (nx < 0 || nx >= n || ny < 0 || ny >= m || mp[nx][ny] == -1) continue;
			if (d[id][nx][ny] > d[id][u.x][u.y] + mp[nx][ny]) {
				d[id][nx][ny] = d[id][u.x][u.y] + mp[nx][ny];
				if (!vis[nx][ny]) {
					vis[nx][ny] = true;
					q.push((node) {nx, ny});
				}
			} 
		}
	}
}

node thr[20];
int ex[MAXN], dp[(1 << 13) + 10][20];

int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &n, &m);
		for (int i = 0; i < n; i++)
			for (int j = 0; j < m; j++)
				scanf("%d", &mp[i][j]);
		scanf("%d", &k);
		for (int id = 0; id < k; id++) {
			scanf("%d%d", &thr[id].x, &thr[id].y);
			spfa(thr[id], id);
			ex[id] = INF;
			for (int i = 0; i < n; i++) 
				ex[id] = min(ex[id], min(d[id][i][0], d[id][i][m - 1]));
			for (int j = 0; j < m; j++)
				ex[id] = min(ex[id], min(d[id][0][j], d[id][n - 1][j]));
		}
		for (int S = 0; S < (1 << k); S++) {
			fill(dp[S], dp[S] + k, INF);
		}
		for (int i = 0; i < k; i++) 
			dp[(1 << i)][i] = ex[i] + mp[thr[i].x][thr[i].y];
		for (int S = 0; S < (1 << k); S++) {
			for (int v = 0; v < k; v++) {
				if (!(S & (1 << v))) continue;
				int x = thr[v].x, y = thr[v].y;
				for (int u = 0; u < k; u++) {
					if (!((S ^ (1 << v)) & (1 << u))) continue;
					dp[S][v] = min(dp[S][v], dp[S ^ (1 << v)][u] + d[u][x][y]);
				}
			}
		}
		int ans = INF;
		for (int i = 0; i < k; i++) {
			ans = min(ans, dp[(1 << k) - 1][i] + ex[i]);
		}
	 	printf("%d\n", ans);
	}
	return 0;
}


你可能感兴趣的:(状压dp,最短路)