洛谷4294 [WC2008]游览计划——斯坦纳树
题目:https://www.luogu.org/problemnew/show/P4294
大概是状压。两种转移,一个是以同一个点为中心,S由自己的子集拼起来;一个是S相同、中心不同的同层转移。
注意S由自己的子集拼起来的时候要减去一倍的自己位置的代价。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } int Mn(int a,int b){return a<b?a:b;} const int N=105,M=(1<<10)+5,K=15,INF=N*(1<<16); int n,m,tot,bh[K][K],x[N],y[N],w[N],dp[N][M],bin[K]; queue<int> q; bool ins[N]; int to[N][M][2][2]; char ch[K][K]; void cz(int k,int d,int s) { dp[d][s]=dp[k][s]+w[d]; to[d][s][0][0]=k;to[d][s][0][1]=s;to[d][s][1][0]=to[d][s][1][1]=0; if(!ins[d])q.push(d),ins[d]=1; } void spfa(int s) { for(int i=1;i<=tot;i++) q.push(i),ins[i]=1; while(q.size()) { int k=q.front(),d;q.pop();ins[k]=0; if(x[k]>1&&dp[d=bh[x[k]-1][y[k]]][s]>dp[k][s]+w[d])cz(k,d,s); if(x[k]<n&&dp[d=bh[x[k]+1][y[k]]][s]>dp[k][s]+w[d])cz(k,d,s); if(y[k]>1&&dp[d=bh[x[k]][y[k]-1]][s]>dp[k][s]+w[d])cz(k,d,s); if(y[k]<m&&dp[d=bh[x[k]][y[k]+1]][s]>dp[k][s]+w[d])cz(k,d,s); } } void solve(int cr,int s) { ch[x[cr]][y[cr]]='o'; if(to[cr][s][0][0])solve(to[cr][s][0][0],to[cr][s][0][1]); if(to[cr][s][1][0])solve(to[cr][s][1][0],to[cr][s][1][1]); } int main() { n=rdn();m=rdn();int cnt=0;bin[0]=1; memset(dp,0x3f,sizeof dp); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { bh[i][j]=++tot;w[tot]=rdn(); x[tot]=i; y[tot]=j; if(w[tot])continue; cnt++; bin[cnt]=bin[cnt-1]<<1; dp[tot][bin[cnt-1]]=0; } for(int s=1;s<bin[cnt];s++) { for(int i=1;i<=tot;i++) for(int d=(s-1)&s;d;d=(d-1)&s) if(dp[i][d]+dp[i][s^d]-w[i]<dp[i][s])//-w[i]! { dp[i][s]=dp[i][d]+dp[i][s^d]-w[i]; to[i][s][0][0]=to[i][s][1][0]=i; to[i][s][0][1]=d; to[i][s][1][1]=s^d; } spfa(s); } int ans=INF,cr[2]={0,bin[cnt]-1}; for(int i=1;i<=tot;i++) if(dp[i][bin[cnt]-1]<ans)ans=dp[i][bin[cnt]-1],cr[0]=i; solve(cr[0],cr[1]); printf("%d\n",ans); for(int i=1;i<=n;i++,puts("")) for(int j=1;j<=m;j++) if(!w[bh[i][j]])putchar('x'); else if(ch[i][j]=='o')putchar('o'); else putchar('_'); return 0; }