【BZOJ2595】[Wc2008]游览计划 斯坦纳树
【BZOJ2595】[Wc2008]游览计划
Description
Input
第一行有两个整数,N和 M,描述方块的数目。
接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点;
否则表示控制该方块至少需要的志愿者数目。 相邻的整数用 (若干个) 空格隔开,
行首行末也可能有多余的空格。
Output
由 N + 1行组成。第一行为一个整数,表示你所给出的方案
中安排的志愿者总数目。
接下来 N行,每行M 个字符,描述方案中相应方块的情况:
z ‘_’(下划线)表示该方块没有安排志愿者;
z ‘o’(小写英文字母o)表示该方块安排了志愿者;
z ‘x’(小写英文字母x)表示该方块是一个景点;
注:请注意输出格式要求,如果缺少某一行或者某一行的字符数目和要求不
一致(任何一行中,多余的空格都不允许出现) ,都可能导致该测试点不得分。
Sample Input
4 4
0 1 1 0
2 5 5 1
1 5 5 1
0 1 1 0
0 1 1 0
2 5 5 1
1 5 5 1
0 1 1 0
Sample Output
6
xoox
___o
___o
xoox
xoox
___o
___o
xoox
HINT
对于100%的数据,N,M,K≤10,其中K为景点的数目。输入的所有整数均在[0,2^16]的范围内
题解:学了一发斯坦纳树。
用f[S][i][j]表示已经连通的景点状态为S,当前处于(i,j)的最小花费,那么转移方程如下:
f[S][i][j]=min{f[S'][i][j]+f[S^S'][i][j]-a[i][j]}
f[S][i][j]=min{f[S][i'][j']+a[i][j]}
第一个方程相当于枚举子集,而第二个方程存在环,所以采用最短路解决。于是我们先枚举子集,每更新一次都跑一边最短路即可。
#include <cstdio> #include <cstring> #include <iostream> #include <queue> using namespace std; int n,m,K,cnt,ans,last; int f[1<<10][12][12],v[12][12],pre[1<<10][12][12],inq[12][12],p[12],Log[1<<10],ref[1<<10],vis[12][12]; int dx[]={1,0,-1,0},dy[]={0,1,0,-1}; queue<int> qx,qy; inline void spfa(int S,int x,int y) { qx.push(x),qy.push(y); int i,tx,ty; while(!qx.empty()) { x=qx.front(),y=qy.front(),qx.pop(),qy.pop(),inq[x][y]=0; for(i=0;i<4;i++) { tx=x+dx[i],ty=y+dy[i]; if(tx>=0&&ty>=0&&tx<n&&ty<m&&f[S][tx][ty]>f[S][x][y]+v[tx][ty]) { f[S][tx][ty]=f[S][x][y]+v[tx][ty],pre[S][tx][ty]=x*m+y; if(!inq[tx][ty]) inq[tx][ty]=1,qx.push(tx),qy.push(ty); } } } } void dfs(int S,int i,int j) { vis[i][j]=1; if(pre[S][i][j]==12345) return ; if(pre[S][i][j]<0) dfs(-pre[S][i][j],i,j),dfs(S^(-pre[S][i][j]),i,j); else dfs(S,pre[S][i][j]/m,pre[S][i][j]%m); } int main() { scanf("%d%d",&n,&m); int i,j,x,y; memset(f,0x3f,sizeof(f)); for(i=0;i<n;i++) for(j=0;j<m;j++) scanf("%d",&v[i][j]); for(i=0;i<n;i++) for(j=0;j<m;j++) if(!v[i][j]) f[1<<K][i][j]=0,pre[1<<K][i][j]=12345,spfa(1<<K,i,j),K++; for(i=0;i<K;i++) Log[1<<i]=i; for(x=1;x<(1<<K);x++) { for(cnt=0,i=x;i;i-=i&-i) p[cnt++]=i&-i; for(y=1;y<(1<<cnt);y++) { ref[y]=ref[y^(y&-y)]|p[Log[y&-y]]; for(i=0;i<n;i++) for(j=0;j<m;j++) if(f[ref[y]][i][j]+f[x^ref[y]][i][j]-v[i][j]<f[x][i][j]) f[x][i][j]=f[ref[y]][i][j]+f[x^ref[y]][i][j]-v[i][j],pre[x][i][j]=-ref[y],spfa(x,i,j); } } ans=1<<30; for(i=0;i<n;i++) for(j=0;j<m;j++) if(f[(1<<K)-1][i][j]<ans) ans=f[(1<<K)-1][i][j],last=i*m+j; dfs((1<<K)-1,last/m,last%m); printf("%d\n",ans); for(i=0;i<n;i++) { for(j=0;j<m;j++) { if(!v[i][j]) putchar('x'); else if(vis[i][j]) putchar('o'); else putchar('_'); } puts(""); } return 0; }//4 4 0 1 1 0 2 5 5 1 1 5 5 1 0 1 1 0
| 欢迎来原网站坐坐! >原文链接<