bzoj2595 [Wc2008]游览计划

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



Sample Output

6
xoox
___o
___o
xoox

HINT

 

 对于100%的数据,N,M,K≤10,其中K为景点的数目。输入的所有整数均在[0,2^16]的范围内

 
 
思路: 本题是最小斯坦纳树的模板题,难点在于需要记录方案。 

       令f[i][j][k]表示已经连接的景点的集合为k时,包含点a[i][j]的最小值。即以(i,j)为根时,景点集合为k时的斯坦纳树。然后有两种转移:

       1.由两个子集合并得到集合k,即f[i][j][k]=f[i][j][x]+f[i][j][y]-a[i][j],x|y=k;

       2.由根的转移得到,即f[i][j][k]=f[x][y][k]+a[i][j],其中(i,j)和(x,y)相邻。

最小斯坦纳树有多种转移方法,要注意区分,这里是按照相同状态转移和子状态转移两种情况来考虑,相同状态转移就需要用到最短路径算法了,注意f[i][j][k]里面i,j这个位置并不一定体现在k这个二进制里面,这也是我之前一直有点疑惑的原因。 

 1 #include<bits/stdc++.h>
 2 using namespace std;  
 3 int const inf=1e9;  
 4 int const N=12;  
 5 int const M=1105;  
 6 int n,m,a[N][N],f[N][N][M],pre[N][N][M][3],h[M+5][2],p; 
 7 bool  bo[N][N];  
 8 int dx[4]={0,0,1,-1};  
 9 int dy[4]={1,-1,0,0};  
10 void dfs(int x,int y,int k){
11   if(!k) return;  
12   bo[x][y]=1;  
13   dfs(pre[x][y][k][0],pre[x][y][k][1],pre[x][y][k][2]);  
14   if(pre[x][y][k][0]==x && pre[x][y][k][1]==y)   
15     dfs(x,y,k^pre[x][y][k][2]); 
16 }  
17 void work(int x,int y){
18   printf("%d\n",f[x][y][(1<<p)-1]);  
19   memset(bo,0,sizeof(bo));  
20   dfs(x,y,(1<<p)-1);  
21   for(int i=1;i<=m;i++){
22     for(int j=1;j<=n;j++)  
23       if(a[i][j])  putchar(bo[i][j]?  'o':'_');
24       else  putchar('x');
25     printf("\n"); 
26   } 
27 }    
28   
29 int main(){
30   scanf("%d%d",&m,&n);  
31   int k,x,y;  
32   for(int i=1;i<=m;i++) 
33     for(int j=1;j<=n;j++)  
34       for(int k=0;k<(1<<10);k++) f[i][j][k]=inf;  
35   for(int i=1;i<=m;i++) 
36     for(int j=1;j<=n;j++) {
37       scanf("%d",&a[i][j]);  
38       if(a[i][j]==0)
39         f[i][j][1<<p]=0,p++; 
40     } 
41   for(int k=1;k<(1<<p);k++){
42     int l=0,r=0;  
43     memset(bo,1,sizeof(bo)); 
44     for(int i=1;i<=m;i++) for(int j=1;j<=n;j++){
45         for(int x=k&(k-1);x;x=(x-1)&k){
46           int tmp=f[i][j][x]+f[i][j][k^x]-a[i][j];  
47           if(tmp<f[i][j][k]){
48             f[i][j][k]=tmp;  
49             pre[i][j][k][0]=i;  
50             pre[i][j][k][1]=j;  
51             pre[i][j][k][2]=x; 
52           } 
53         } 
54         if(f[i][j][k]<inf){
55             h[++r][0]=i;  
56             h[r][1]=j;  
57             bo[i][j]=0;  
58         }  
59       }
60     while (l!=r){
61       l=l%M+1;  
62       x=h[l][0]; y=h[l][1];  bo[x][y]=1;  
63       for(int i=0;i<4;i++){
64         int u=x+dx[i]; 
65         int v=y+dy[i];  
66         if(u<1 || u>m || v<1 || v>n) continue; 
67         if(f[x][y][k]+a[u][v]<f[u][v][k]){
68           f[u][v][k]=f[x][y][k]+a[u][v]; 
69           pre[u][v][k][0]=x;  
70           pre[u][v][k][1]=y; 
71           pre[u][v][k][2]=k;  
72           if(bo[u][v]){
73             bo[u][v]=0; r=r%M+1;  
74             h[r][0]=u;h[r][1]=v;  
75           } 
76         }
77       }  
78     } 
79   } 
80   for(int i=1;i<=m;i++)  
81     for(int j=1;j<=n;j++)  
82       if(a[i][j]==0){
83         work(i,j);  
84         return 0; 
85       } 
86   return 0; 
87 }  
88   
View Code

 

posted @ 2019-08-15 02:52  zjxxcn  阅读(165)  评论(0编辑  收藏  举报