洛谷P4294 [WC2008]游览计划 - 斯坦纳树

题意:洛谷P4294

给定一个矩阵,问一条最小权值路径能够走遍所有景点,并输出最小权值与路径。

分析:

斯坦纳树是什么?(最小生成树是斯坦塔树的一种特殊情况)
按照我的理解,斯坦纳树通常与状压 d p dp dp和最短路联系在一起。

斯坦纳树中的状压 d p dp dp分为两部分:
首先定义状态:
d p [ i ] [ j ] dp[i][j] dp[i][j]表示选中 i i i点为根必须点的状态为 j j j的最小权值,
特别地, d p [ i ] [ 1 &lt; &lt; j ] = 0 dp[i][1&lt;&lt;j]=0 dp[i][1<<j]=0
那么有
1. 1. 1.自己由自己的子集转移而来
d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i ] [ s ] + d p [ i ] [ k ] ) dp[i][j]=min(dp[i][j],dp[i][s]+dp[i][k]) dp[i][j]=min(dp[i][j],dp[i][s]+dp[i][k]) s s s ⊆ \subseteq j j j k k k ∁ \complement s s s
s s s j j j的真子集, k k k s s s的补集。
2. 2. 2.由同一集合不同路径转移而来
d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ k ] [ j ] + e d g e [ k dp[i][j]=min(dp[i][j],dp[k][j]+edge[k dp[i][j]=min(dp[i][j],dp[k][j]+edge[k → \rightarrow i ] . v a l ) i].val) i].val)
这个方程的意思是 i i i点有可能是由 i i i点相邻的点转移而来的,观察这个方程可以想到最短路松弛操作。故上 d i j k dijk dijk s p f a spfa spfa

由于本题不是边权而是点权,所以方程要做一些微小的变化:
1. 1. 1. d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i ] [ s ] + d p [ i ] [ k ] − p i c [ i ] [ j ] ) dp[i][j]=min(dp[i][j],dp[i][s]+dp[i][k]-pic[i][j]) dp[i][j]=min(dp[i][j],dp[i][s]+dp[i][k]pic[i][j]) s s s ⊆ \subseteq j j j k k k ∁ \complement s s s
2. 2. 2. d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ k ] [ j ] + p i c [ i ] [ j ] ) dp[i][j]=min(dp[i][j],dp[k][j]+pic[i][j]) dp[i][j]=min(dp[i][j],dp[k][j]+pic[i][j])

接下来就是输出路径的问题了,我们用 p a i r &lt; p a i r &lt; i n t , i n t &gt; , i n t &gt; pair&lt;pair&lt;int,int&gt;,int&gt; pair<pair<int,int>,int>记录转移时是从哪转移的即可, p a i r pair pair里的 p a i r pair pair记录点坐标,另外一个记录状态。最后从一个选定的景点 d f s dfs dfs即可。 d f s dfs dfs时注意有自己向自己转移的情况,此时 d f s dfs dfs子集和子集的补集即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

#define pii pair<int, int>
#define ppi pair<pii, int>
#define mp make_pair
#define fi first
#define se second

const int maxn = 10 + 5;
const int maxm = (1 << 10) + 5;
const int inf  = 0x3f3f3f3f;

int n, m, k, x, y, v, from, num, maxsta;
int dp[maxn * maxn][maxm], pic[maxn][maxn], bel[maxn][maxn];
int to[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
bool vis[maxn][maxn], use[maxn][maxn];
pii rt;
ppi pre[maxn * maxn][maxm];
queue<pii> q;

bool check(int x, int y){
	if(x < 1 || y < 1 || x > n || y > m) return false;
	return true;
}

void spfa(int sta){
	while(!q.empty()){
		pii u = q.front();
		q.pop();
		vis[u.fi][u.se] = 0;
		from = bel[u.fi][u.se];
		for(int i = 0; i < 4; i++){
			x = u.fi + to[i][0];
			y = u.se + to[i][1];
			v = bel[x][y];
			if(check(x, y)){
				if(dp[v][sta] > dp[from][sta] + pic[x][y]){
					dp[v][sta] = dp[from][sta] + pic[x][y];
					pre[v][sta] = mp(mp(u.fi, u.se), sta);
					if(!vis[x][y]) q.push(mp(x, y)), vis[x][y] = 1;
				}
			}
		}
	}
}

void dfs(pii rt, int sta){
	int u = bel[rt.fi][rt.se];
	if(!pre[u][sta].se) return ;
	use[rt.fi][rt.se] = true;
	if(pre[u][sta].fi == rt) dfs(rt, pre[u][sta].se ^ sta);
	dfs(pre[u][sta].fi, pre[u][sta].se);
}

int main(){
    scanf("%d %d", &n, &m);
	memset(dp, inf, sizeof dp);
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			scanf("%d", &pic[i][j]);
			bel[i][j] = ++num;
			if(!pic[i][j]) dp[num][1 << (k++)] = 0, rt = mp(i, j);
		}
	}
	maxsta = 1 << k;
	for(int sta = 0; sta < maxsta; sta++){
		for(int i = 1; i <= n; i++){
			for(int j = 1; j <= m; j++){
				int u = bel[i][j];
				for(int s = sta; s; s = (s - 1) & sta){
					if(dp[u][sta] > dp[u][s] + dp[u][sta ^ s] - pic[i][j]){
						dp[u][sta] = dp[u][s] + dp[u][sta ^ s] - pic[i][j];
						pre[u][sta] = mp(mp(i, j), s);
					}
				}
				if(dp[u][sta] < inf) q.push(mp(i, j)), vis[i][j] = 1;
 			}
		}
		spfa(sta);
	}
	printf("%d\n", dp[bel[rt.fi][rt.se]][(1 << k) - 1]);
	dfs(rt, (1 << k) - 1);
	for(int i = 1; i <= n; i++, putchar('\n'))
		for(int j = 1; j <= m; j++)
			if(!pic[i][j]) putchar('x');
			else if(use[i][j]) putchar('o');
			else putchar('_');
    return 0; 
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值