zoukankan      html  css  js  c++  java
  • BZOJ 2595 [Wc2008]游览计划(斯坦纳树)

    斯坦纳树的板子题。

    斯坦纳树问题是组合优化问题,与最小生成树相似,是最短网络的一种。

    最小生成树是在给定的点集和边中寻求最短网络使所有点连通。

    而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小。

    然而我解决问题并不需要你知道什么关于斯坦纳树的知识。
    会状压(子集)DP和最短路就行了。
    设dp[s][i][j]为使s集合中的景点都与点(i,j)相连的最小代价。
    然后转移有:
    (dp[s][i][j]=min(dp[s][i][j],dp[k][i][j]+dp[s)^(k][i][j]-val(i,j)) (ksubset s))
    (dp[s][i][j]=min(dp[s][i][j],min(dp[s][i+1][j],dp[s][i-1][j],dp[s][i][j+1],dp[s][i][j-1])+val(i,j)))
    然后第一个转移用子集DP,第二个转移用最短路。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    using namespace std;
    const int N=15;
    const int INF=1e9;
    int xx[]={0,0,0,1,-1};
    int yy[]={0,1,-1,0,0};
    struct node{
    	int x,y,w;
    	node(int xx=0,int yy=0,int ww=0){
    		x=xx;y=yy;w=ww;
    	}
    };
    struct from{
    	int a,b,c;
    	from(int aa=0,int bb=0,int cc=0){
    		a=aa,b=bb,c=cc;
    	}
    }pre[1111][N][N];
    bool operator <(node a,node b){
    	return a.w>b.w;
    }
    priority_queue<node> q;
    bool vis[N][N];
    int n,m,a[N][N],t,dp[1111][N][N];
    void dij(int k){
    	memset(vis,0,sizeof(vis));
    	while(!q.empty()){
    		node u=q.top();q.pop();
    		int x=u.x,y=u.y;
    		if(vis[x][y])continue;
    		vis[x][y]=1;
    		for(int i=1;i<=4;i++){
    			int X=x+xx[i],Y=y+yy[i];
    			if(X<1||X>n||Y<1||Y>m)continue;
    			if(dp[k][X][Y]>dp[k][x][y]+a[X][Y]){
    				dp[k][X][Y]=dp[k][x][y]+a[X][Y];
    				pre[k][X][Y]=from(k,x,y);
    				q.push(node(X,Y,dp[k][X][Y]));
    			}
    		}
    	}
    }
    int read(){
    	int sum=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    	return sum*f;
    }
    void dfs(int a,int b,int c){
    	vis[b][c]=1;
    	int A=pre[a][b][c].a;
    	int B=pre[a][b][c].b;
    	int C=pre[a][b][c].c;
    	if(A+B+C==0)return;
    	if(A!=a)dfs(A,b,c),dfs(a^A,b,c);
    	else dfs(a,B,C);
    }
    int main(){
    	n=read();m=read();
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++){
    			a[i][j]=read();
    			if(a[i][j]==0)t++;
    		}
    	for(int k=0;k<(1<<t);k++)
    		for(int i=1;i<=n;i++)
    			for(int j=1;j<=m;j++)
    				dp[k][i][j]=INF;
    	t=0;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			if(a[i][j]==0)dp[(1<<t++)][i][j]=0;
    	for(int k=1;k<(1<<t);k++){
    		for(int i=1;i<=n;i++)
    			for(int j=1;j<=m;j++)
    				for(int w=k;w;w=(w-1)&k)
    					if(dp[k][i][j]>dp[k^w][i][j]+dp[w][i][j]-a[i][j]){
    						dp[k][i][j]=dp[k^w][i][j]+dp[w][i][j]-a[i][j];
    						pre[k][i][j]=from(w,i,j);
    					}
    		for(int i=1;i<=n;i++)
    			for(int j=1;j<=m;j++)
    				if(dp[k][i][j]!=INF)q.push(node(i,j,dp[k][i][j]));
    		dij(k);
    	}
    	int ans=INF,ida,idb,idc;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			if(ans>dp[(1<<t)-1][i][j]){
    				ans=dp[(1<<t)-1][i][j];
    				ida=(1<<t)-1,idb=i,idc=j;
    			}
    	printf("%d
    ",ans);
    	memset(vis,0,sizeof(vis));
    	dfs(ida,idb,idc);
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++)
    			if(a[i][j]==0)cout<<'x';
    			else if(vis[i][j])cout<<'o';
    			else cout<<'_';
    		printf("
    ");
    	}
    	return 0;
    }
    
  • 相关阅读:
    HTTP状态码详解
    phpcms v9调用多个栏目下文章的方法
    纯CSS3制作学生入学档案表单样式代码
    phpcmsv9 标题颜色显示问题
    Excel中利用IF和TIME函数计算出上下班状态!
    excel if判断时间段早晚班
    PHPCMS列表页伪静态
    EXCEL IF 函数 模糊查询
    如何用js实现截取一个字符串中的数字
    Elasticsearch学习之基本核心概念
  • 原文地址:https://www.cnblogs.com/Xu-daxia/p/10436891.html
Copyright © 2011-2022 走看看