LG 4294[WC2008]游览计划(最小斯坦纳树)
LG 4294[WC2008]游览计划
前言
我就喜欢这种重题的题,不知道是WC搬的CF还是CF搬的WC,不管了,反正一样就是了。
既可以水 \(2\) 篇题解,也可以搞掉一黑一紫,何乐而不为?
解题思路
\(k\) 很小,又是连通性问题,显然最小斯坦纳树,问题是如何建图?如何求出方案?
建图
我们可以按四联通来建图,不用管边权。由于在这张图里只有点权,我们的斯坦纳树 DP 转移方程会有所改变(不要重复计算点权),具体看代码,其实很好理解(只要你理解了斯坦纳树的板子)。
求出方案
这个有点难度。其实可以和大部分 DP 一样,需要记录一下转移决策点。SPFA 可以这样,三角形不等式的部分怎么办?
我们其实可以这样,如果可以已通过 SPFA 记录的决策点一直往前推,就往前,直到结束为止,我们就在把当前状态的递推在跑一遍即可。
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
const int MAXN=110,MAXK=11,INF=2e9;
const int dx[]={1,0,-1,0};
const int dy[]={0,1,0,-1};
int n,m,k,cnt;
int a[MAXN],h[MAXN],f[MAXN][1<<MAXK],g[MAXN][1<<MAXK];
char ans[MAXN];
bool inq[MAXN],vis[MAXN][1<<MAXK];
queue<int> q;
int idx(int i,int j) {
return (i-1)*m+j;
}
bool ok(int i,int j) {
if(i>n||i<1||j>m||j<1) {
return 0;
}
return 1;
}
struct edge {
int to,nxt;
}e[10000];
void addedge(int u,int v) {
e[++cnt].nxt=h[u];
e[cnt].to=v;
h[u]=cnt;
}
void SPFA(int s) {
while(!q.empty()) {
int u=q.front();
q.pop();
inq[u]=0;
for(int i=h[u];i;i=e[i].nxt) {
int v=e[i].to;
if(f[v][s]>f[u][s]+a[v]) {
f[v][s]=f[u][s]+a[v];
g[v][s]=u;
if(!inq[v]) {
q.push(v);
inq[v]=1;
}
}
}
}
}
void getans(int u,int s) {
if(vis[u][s]) {
return ;
}
vis[u][s]=1;
ans[u]='o';
//一直往前推
while(g[u][s]!=-1&&f[g[u][s]][s]+a[u]==f[u][s]) {
u=g[u][s];
getans(u,s);
}
for(int t=s&(s-1);t;t=(t-1)&s) {
if(f[u][t]+f[u][s^t]-a[u]==f[u][s]) {
getans(u,t);
getans(u,s^t);
break;
//重新递推
}
}
}
signed main() {
cin>>n>>m;
for(int i=1;i<=n*m;i++) {
for(int j=0;j<=1023;j++) {
f[i][j]=INF;
g[i][j]=-1;
}
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
ans[idx(i,j)]='_';
cin>>a[idx(i,j)];
if(!a[idx(i,j)]) {
f[idx(i,j)][1<<k]=0;
k++;
}
for(int p=0;p<4;p++) {
if(ok(i+dx[p],j+dy[p])) {
addedge(idx(i,j),idx(i+dx[p],j+dy[p]));
}
}
}
}
int mx=(1<<k)-1;
for(int s=0;s<=mx;s++) {
for(int i=1;i<=n*m;i++) {
for(int t=s&(s-1);t;t=(t-1)&s) {
f[i][s]=min(f[i][s],f[i][t]+f[i][s^t]-a[i]);
//同一点的点权不要重复算
}
if(f[i][s]<INF) {
inq[i]=1;
q.push(i);
}
}
SPFA(s);
}
int minn=INF,mark=0;
for(int i=1;i<=n*m;i++) {
if(f[i][mx]<minn) {
minn=f[i][mx];
mark=i;
}
}
cout<<minn<<endl;
getans(mark,mx);
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
if(!a[idx(i,j)]) {
ans[idx(i,j)]='x';
}
printf("%c",ans[idx(i,j)]);
}
puts("");
}
return 0;
}