zoukankan      html  css  js  c++  java
  • BZOJ2595 [Wc2008]游览计划

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

    本文作者:ljh2000
    作者博客:http://www.cnblogs.com/ljh2000-jump/
    转载请注明出处,侵权必究,保留最终解释权!

     

    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



    Sample Output

    6
    xoox
    ___o
    ___o
    xoox

    HINT

     

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

     
     
    正解:斯坦纳树
    解题报告:
      斯坦纳树入门题…
      考虑我把这些关键点用二进制状态表示,那么我可以用状压的方式来转移。
      f[i][j][s]表示连通性至少为s,且联通出来的树的根在(i,j)的最优值,转移的话就是枚举s的子集,然后将s-子集和子集两个部分合并。
      还有一种转移,同状态之间的连通树换根,同层之间SPFA转移即可。
      这道题需要记录方案,记一下每个状态的pre前驱即可。
     
     
    //It is made by ljh2000
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <ctime>
    #include <vector>
    #include <queue>
    #include <map>
    #include <set>
    #include <string>
    #include <complex>
    using namespace std;
    typedef long long LL;
    const int MAXN = 12;
    const int MAXS = 1024;
    const int MAXM = 1000011;
    int n,m,k,ju[MAXN][MAXN],S,dui[MAXM][2],head,tail,a[MAXN][MAXN];
    int f[MAXN][MAXN][MAXS];//f[i][j][s]表示连通性至少为s,且联通出来的树的根在(i,j)的最优值
    int pre[MAXN][MAXN][MAXS][3];//记录前驱状态
    bool vis[MAXN][MAXN];
    int dx[4]={1,-1,0,0};
    int dy[4]={0,0,1,-1};
    
    inline int getint(){
        int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
        if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
    }
    
    inline void print(){
    	for(int i=1;i<=n;i++) {
    		for(int j=1;j<=m;j++) {
    			if(ju[i][j]==0) printf("x");
    			else if(a[i][j]) printf("o");
    			else printf("_");
    		}
    		puts("");
    	}
    }
    
    inline void dfs(int x,int y,int s){
    	if(x==0 || y==0) return ;
    	a[x][y]=1; dfs(pre[x][y][s][0],pre[x][y][s][1],pre[x][y][s][2]);
    	//有两个方向!!!
    	if(pre[x][y][s][0]==x && pre[x][y][s][1]==y) dfs(x,y,s-pre[x][y][s][2]);//两个状态在共用根的情况下进行合并
    }
    
    inline void SPFA(int s){
    	head=tail=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(f[i][j][s]!=f[0][0][0]) { vis[i][j]=1; dui[++tail][0]=i; dui[tail][1]=j; }
    	int ux,uy,nowx,nowy;
    	while(head<tail) {
    		head++; ux=dui[head][0]; uy=dui[head][1]; vis[ux][uy]=0;
    		for(int i=0;i<4;i++) {
    			nowx=ux+dx[i]; nowy=uy+dy[i]; if(nowx<=0 || nowy<=0 || nowx>n || nowy>m/*!!!*/) continue;
    			if(f[nowx][nowy][s]>f[ux][uy][s]+ju[nowx][nowy]) {
    				f[nowx][nowy][s]=f[ux][uy][s]+ju[nowx][nowy];
    				pre[nowx][nowy][s][0]=ux;
    				pre[nowx][nowy][s][1]=uy;
    				pre[nowx][nowy][s][2]=s;
    				if(!vis[nowx][nowy]) {
    					vis[nowx][nowy]=1;
    					dui[++tail][0]=nowx;
    					dui[tail][1]=nowy;
    				}
    			}
    		}
    	}
    }
    
    inline void work(){
    	n=getint(); m=getint();
    	memset(f,0x3f,sizeof(f));
    	for(int i=1;i<=n;i++) 
    		for(int j=1;j<=m;j++) {
    			ju[i][j]=getint();
    			if(ju[i][j]==0) f[i][j][(1<<k)]=0,k++;
    		}
    
    	S=(1<<k)-1; int now;
    	if(k==0) {
    		for(int i=1;i<=n;i++) {
    			for(int j=1;j<=m;j++)
    				if(ju[i][j]) printf("_");
    			puts("");
    		}
    		return ;
    	}
    
    	for(int s=1;s<=S;s++) {
    		for(int i=1;i<=n;i++)
    			for(int j=1;j<=m;j++)
    				for(int from=(s-1)&s;from;from=(from-1)&s) {//枚举子集
    					if(f[i][j][from]==f[0][0][0] || f[i][j][s-from]==f[0][0][0]) continue;
    					now=f[i][j][from]+f[i][j][s-from]-ju[i][j];
    					if(now<f[i][j][s]) {
    						f[i][j][s]=now;
    						pre[i][j][s][0]=i; pre[i][j][s][1]=j; pre[i][j][s][2]=from;
    					}
    				}
    		SPFA(s);
    	}
    
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			if(ju[i][j]==0) {
    				printf("%d
    ",f[i][j][S]);
    				dfs(i,j,S);
    				print();
    				return ;
    			}
    }
    
    int main()
    {
        work();
        return 0;
    }
    

      

  • 相关阅读:
    【题解】[HEOI2016/TJOI2016]字符串
    【题解】CF1037H Security
    Centos 7开机自启动oracle
    WRH$_ACTIVE_SESSION_HISTORY打补丁14084247实现自动分区
    oracle设置awr采集间隔和保留时间
    dgbroker配置Fast-Start Failover
    dgbroker删除后切换为手工管理
    删除dgbroker
    现有dgbroker管理的dg下添加一台从库且互相切换
    linux-unzip-error
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6418187.html
Copyright © 2011-2022 走看看