洛谷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
",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;
}
原文地址:https://www.cnblogs.com/Narh/p/10235313.html