题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2595
今天刚学了斯坦纳树,还不太会,写一道题练习一下;
参考了博客:http://www.cnblogs.com/lazycal/archive/2013/08/31/bzoj-2595.html
代码也是模仿着写的,感觉有了更深的理解;
总之,大概就是两种转移方式,合并转移枚举子集即可,最短路转移用 spfa;
还要记录一个 pre 用来找到连通块内的点;
用哈希一样的方法把几个数 pack 起来的写法真神奇啊;
说实话还是有点云里雾里,不过成功写出了第一道斯坦纳树的题,感觉很好!
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; int const maxn=15,inf=0x3f3f3f3f; int n,m,K,mx,f[maxn][maxn][1<<maxn],a[maxn][maxn],pre[maxn][maxn][1<<maxn]; int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1}; bool vis[maxn][maxn],inq[maxn*maxn]; queue<int>q; int pack(int i,int j){return i*10+j;} int pack2(int i,int j,int s){return i*100000+j*10000+s;} void unpack(int x,int &i,int &j){i=x/10; j=x%10;} void unpack2(int x,int &i,int &j,int &s){i=x/100000; j=(x/10000)%10; s=x%10000;} bool update(int x,int y,int s,int i,int j,int sta,int w) { if(f[x][y][s]>w){f[x][y][s]=w; pre[x][y][s]=pack2(i,j,sta); return 1;} return 0; } void spfa(int sta) { while(q.size()) { int i,j; unpack(q.front(),i,j); inq[q.front()]=0; q.pop(); for(int k=0;k<4;k++) { int x=i+dx[k],y=j+dy[k],tmp; if(x==-1||y==-1||x==n||y==m)continue;//!!! if(update(x,y,sta,i,j,sta,f[i][j][sta]+a[x][y])&&!inq[tmp=pack(x,y)])//不改变连通性 q.push(tmp),inq[tmp]=1; } } } void dfs(int x,int y,int sta) { if(!pre[x][y][sta])return; vis[x][y]=1; int i,j,s; unpack2(pre[x][y][sta],i,j,s); dfs(i,j,s); if(i==x&&j==y)dfs(i,j,sta-s);//合并转移 } void print() { for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { if(!a[i][j])printf("x"); else if(vis[i][j])printf("o"); else printf("_"); } printf(" "); } } int main() { scanf("%d%d",&n,&m); memset(f,0x3f,sizeof f); for(int i=0;i<n;i++) for(int j=0;j<m;j++) { scanf("%d",&a[i][j]); if(!a[i][j])f[i][j][1<<(K++)]=0; } mx=(1<<K); for(int sta=1;sta<mx;sta++) { for(int i=0;i<n;i++) for(int j=0,tmp;j<m;j++)//以(i,j)为媒介 { for(int s=(sta&(sta-1));s;s=(s-1)&sta) update(i,j,sta,i,j,s,f[i][j][s]+f[i][j][sta-s]-a[i][j]);//点权重复 if(f[i][j][sta]!=inf)q.push(tmp=pack(i,j)),inq[tmp]=1;//inq! } spfa(sta);//更新不同位置的sta } for(int i=0;i<n;i++) for(int j=0;j<m;j++) if(!a[i][j]) { printf("%d ",f[i][j][mx-1]); dfs(i,j,mx-1); print(); return 0; } }