bzoj2595: [Wc2008]游览计划

最小斯坦纳树的模板题

传送门

  1 //Achen
  2 #include<algorithm>
  3 #include<iostream>
  4 #include<cstring>
  5 #include<cstdlib>
  6 #include<vector>
  7 #include<cstdio>
  8 #include<queue>
  9 #include<cmath>
 10 #include<set>
 11 #include<map>
 12 #define Formylove return 0
 13 #define For(i,a,b) for(int i=(a);i<=(b);i++)
 14 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
 15 const int N=1050;
 16 typedef long long LL;
 17 typedef double db;
 18 using namespace std;
 19 int n,m,a[12][12],tot,sx,sy,f[12][12][N];
 20 
 21 template<typename T>void read(T &x)  {
 22     char ch=getchar(); x=0; T f=1;
 23     while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
 24     if(ch=='-') f=-1,ch=getchar();
 25     for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
 26 }
 27 
 28 struct pre {
 29     int i,j,s;
 30     pre(int i=0,int j=0,int s=0):i(i),j(j),s(s){}
 31 }p[12][12][N];
 32 #define pr pair<int,int>
 33 #define fi first
 34 #define se second
 35 
 36 queue<pr>que;
 37 int vis[12][12],tx[5]={0,0,1,-1},ty[5]={1,-1,0,0};
 38 void spfa(int S) {
 39     while(!que.empty()) {
 40         pr x=que.front();
 41         vis[x.fi][x.se]=0;
 42         que.pop();
 43         For(t,0,3) {
 44             int xx=x.fi+tx[t],yy=x.se+ty[t];
 45             if(xx<1||xx>n||yy<1||yy>m) continue;
 46             if(f[xx][yy][S]>f[x.fi][x.se][S]+a[xx][yy]) {
 47                 f[xx][yy][S]=f[x.fi][x.se][S]+a[xx][yy];
 48                 p[xx][yy][S]=pre(x.fi,x.se,S);
 49                 if(!vis[xx][yy])
 50                     que.push(make_pair(xx,yy)); 
 51             }
 52         }
 53     }
 54 }
 55 
 56 void dfs(int x,int y,int s) {
 57     pre pp=p[x][y][s];
 58     vis[x][y]=1;
 59     int xx=pp.i,yy=pp.j,ss=pp.s;
 60     if(xx==0&&yy==0) return;
 61     dfs(xx,yy,ss);
 62     if(x==xx&&y==yy) dfs(xx,yy,s-ss);
 63 }
 64 
 65 int main() {
 66 #ifdef ANS
 67     freopen(".in","r",stdin);
 68     freopen(".out","w",stdout);
 69 #endif
 70     read(n); read(m);
 71     memset(f,127/3,sizeof(f));
 72     int inf=f[0][0][0];
 73     For(i,1,n) For(j,1,m) {
 74         read(a[i][j]);
 75         if(!a[i][j]) {
 76             f[i][j][1<<tot]=0;
 77             tot++;
 78             sx=i; sy=j;
 79         }
 80     }
 81     int up=(1<<tot)-1;
 82     For(S,0,up) {
 83         For(i,1,n) For(j,1,m) {
 84             for(int ss=((S-1)&S);ss;ss=((ss-1)&S)) 
 85                 if(f[i][j][ss]+f[i][j][S-ss]-a[i][j]<f[i][j][S]) {
 86                     f[i][j][S]=f[i][j][ss]+f[i][j][S-ss]-a[i][j];
 87                     p[i][j][S]=pre(i,j,ss);
 88                 }
 89             if(f[i][j][S]!=inf) {
 90                 vis[i][j]=1;
 91                 que.push(make_pair(i,j));
 92             }
 93         }
 94         spfa(S);
 95     }
 96     printf("%d\n",f[sx][sy][up]);
 97     dfs(sx,sy,up);
 98     For(i,1,n) For(j,1,m) {
 99         if(!a[i][j]) putchar('x');
100         else if(vis[i][j]) putchar('o');
101         else putchar('_');
102         if(j==m) puts("");
103     }
104     Formylove;
105 }
View Code

最近bug真的好多啊,难得1A。

每次看以前忘了的模板的时候看到自己博客就模板题三个字的时候就很想打自己。所以我得口胡两句。

 

完全不严谨的口胡:

最小斯坦纳树大概是网格图求一个给定点集的最小网络,并且允许在给定点集之外选择一些点。

一般k很小,k<=10的样子,可以用状压dp求解。

f[i][s]表示以点i为根,已和s集合里的点联通的生成树的最小代价。

两种转移,

1、i的不同子树的合并,f[i][s]=min{ f[i][s']+f[i][s-s']-cost[i] };

2、考虑添加给定子集外的点的情况,f[i][s]=f[j][s]+a[i][j]; 从一棵合法的树一个点一个点向外扩张,

 比如这棵树,褐色是给定点集,下面两个褐色点联通后从中间的红点向上扩张,在最上面的褐点的位置再用第一种转移合并即可得到以最上面的褐点为根的这棵树。

 

 

 

posted @ 2018-08-21 20:40  啊宸  阅读(188)  评论(0编辑  收藏  举报