zoukankan      html  css  js  c++  java
  • WC 2008 观光计划(斯坦纳树)

    题意

    https://www.lydsy.com/JudgeOnline/problem.php?id=2595

    思路

    是一道比较裸的斯坦纳树呢~

    题意等价于选出包含一些点的最小生成树,这就是斯坦纳树的功能。

    举个例子,给定 (n) 个点,其中 (k) 个点被称作关键点,(m) 条带权边,求原图的一个权值最小的子图,这张子图图为包含这 (k) 个点的树。

    我们定义 (dp[i][j]) 为关键点集合 (i) 与任意节点 (j) 连通的最小权的树。考虑转移这个 (dp) 数组,比较显然的是以下的子集划分:

    [dp[i][j]=min(dp[k][j]+dp[isetminus k][j]) ]

    其中 (k)(i) 的子集。

    当然这样转移是不够的,在关键点集合 (i) 不变的情况下,(j) 有可能会发生改变,即发生如下转移:

    [ ext{chk_min}(dp[i][k],dp[i][j]+w(j,k)) ]

    其中 (w(j,k)) 为一条 (j) 指向 (k) 的边的边权。不难发现,这个过程和最短路的松弛操作是一样的,那么就可以利用最短路进行转移,没有负边就跑 ( ext{dijkstra}),否则跑 ( ext{spfa})

    这道题求的东西略微不同,是点有点权,不过无所谓,转移稍稍改动即可。然后还要输出方案,那么在 (dp) 转移的时候还需要记录从哪里转移过来。

    代码

    #include<bits/stdc++.h>
    #define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
    #define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
    template<typename T,typename _T>inline bool chk_min(T &x,const _T y){return y<x?x=y,1:0;}
    template<typename T,typename _T>inline bool chk_max(T &x,const _T y){return x<y?x=y,1:0;}
    typedef long long ll;
    template<const int N,const int M,typename T>struct LinkedList
    {
    	int head[N],nxt[M],tot;T to[M];
    	LinkedList(){clear();}
    	T &operator [](const int x){return to[x];}
    	void clear(){memset(head,-1,sizeof(head)),tot=0;}
    	void add(int u,T v){to[tot]=v,nxt[tot]=head[u],head[u]=tot++;}
    	#define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
    };
    struct node
    {
    	int at,path;
    	bool operator <(const node &_)const{return path>_.path;}
    };
    
    LinkedList<103,103*4,int>G;
    std::priority_queue<node>Q;
    int dp[(1<<10)+3][103];
    bool lasknd[(1<<10)+3][103];
    int las[(1<<10)+3][103];
    bool mark[103];
    int mp[103],ori[13];
    int pw[103];
    int n,m,K;
    
    inline int hs(int x,int y){return x*m+y;}
    
    void Steiner()
    {
    	FOR(i,0,(1<<K)-1)FOR(j,0,n-1)dp[i][j]=1e9;
    	FOR(i,0,K-1)dp[1<<i][ori[i]]=0;
    	FOR(i,1,(1<<K)-1)
    	{
    		FOR(j,0,n-1)
    			for(int k=(i-1)&i;k;k=(k-1)&i)
    				if(chk_min(dp[i][j],dp[k][j]+dp[i^k][j]-pw[j]))
    				{
    					lasknd[i][j]=0;
    					las[i][j]=k;
    				}
    		while(!Q.empty())Q.pop();
    		FOR(j,0,n-1)Q.push((node){j,dp[i][j]});
    		while(!Q.empty())
    		{
    			node now=Q.top();Q.pop();
    			int u=now.at;
    			if(now.path>dp[i][u])continue;
    			EOR(k,G,u)
    			{
    				int v=G[k],w=pw[v];
    				if(chk_min(dp[i][v],dp[i][u]+w))
    				{
    					lasknd[i][v]=1;
    					las[i][v]=u;
    					Q.push((node){v,dp[i][v]});
    				}
    			}
    		}
    	}
    }
    
    void backtrack(int i,int j)
    {
    	mark[j]=1;
    	if(mp[j]!=-1&&i==(1<<mp[j]))return;
    	if(!lasknd[i][j])
    		backtrack(las[i][j],j),backtrack(i^las[i][j],j);
    	else backtrack(i,las[i][j]);
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	FOR(i,0,n-1)FOR(j,0,m-1)
    	{
    		scanf("%d",&pw[hs(i,j)]);
    		if(!pw[hs(i,j)])mp[hs(i,j)]=K,ori[K]=hs(i,j),K++;
    		else mp[hs(i,j)]=-1;
    	}
    	FOR(i,0,n-1)FOR(j,0,m-2)
    	{
    		G.add(hs(i,j),hs(i,j+1));
    		G.add(hs(i,j+1),hs(i,j));
    	}
    	FOR(i,0,n-2)FOR(j,0,m-1)
    	{
    		G.add(hs(i,j),hs(i+1,j));
    		G.add(hs(i+1,j),hs(i,j));
    	}
    	n*=m;
    	Steiner();
    	int ans=1e9,id;
    	FOR(i,0,n-1)if(chk_min(ans,dp[(1<<K)-1][i]))id=i;
    	backtrack((1<<K)-1,id);
    	printf("%d
    ",ans);
    	FOR(i,0,n-1)
    	{
    		if(!pw[i])putchar('x');
    		else putchar(mark[i]?'o':'_');
    		if(i%m==m-1)putchar('
    ');
    	}
    	return 0;
    }
    
  • 相关阅读:
    Post和Get的区别(兼谈页面间传值的方式)
    ClickOnce部署Winform程序的方方面面
    TSQL查询进阶深入浅出视图
    一个java volatile测试揭开的陷阱
    java volatile的一个验证反例
    [Swing扩展组件分享]为JTable添加选择列(CheckBox)的包装类
    JTextField限制输入长度的完美解决方案
    swing程序的关闭机制看好你的swing.Timer,别让它成为程序不能退出的原凶
    举例理解单元测试
    打印出txt中出现频率最高的十个词——软件工程个人项目C语言
  • 原文地址:https://www.cnblogs.com/Paulliant/p/11386625.html
Copyright © 2011-2022 走看看