• BZOJ.2595.[WC2008]游览计划(DP 斯坦纳树)


    题目链接

    f[i][s]表示以i为根节点,当前关键点的连通状态为s(每个点是否已与i连通)时的最优解。i是枚举得到的根节点,有了根节点就容易DP了。
    那么i为根节点时,其状态s的更新为 (f[i][s]=min{f[i][s']+f[i][complement_{s}s']-cost[i]},s'in s)(枚举子集s'后,显然只需要s'的补集。减cost[i]是因为两种状态都包含,cost[i]算重了)
    如果我们想合并入当前连通块一个新的非关键点v并以v为根,那么 (f[v][s]=min{f[k][s]+cost[v]},k,v相邻)
    第一个更新可以按顺序,第二个更新没有明显顺序,但是如果固定状态s,很像SPFA,可类似转移。
    输出方案,可以每次转移记录转移前的点与状态s,因为可能是转移点也可能是用子集更新。最后随便找一个关键点开始DFS即可。

    复杂度 (O(n imes 3^k+cE imes 2^k))
    c为SPFA复杂度中的常数,E为边的数量,但几乎达不到全部边的数量,甚至非常小。(3^k)来自于子集的转移(sum_{i=1}^nC_n^i imes 2^i),用二项式展开求一下和。

    //2080kb	148ms
    #include <queue>
    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    #define gc() getchar()
    #define En(i,j) (i*m+j)//Encode
    #define De(w) mp(w/m,w%m)//Decode
    #define mp std::make_pair
    #define pr std::pair<int,int>
    const int N=102,INF=1e9,to[5]={1,0,-1,0,1};
    
    int n,m,cost[N],f[(1<<10)+1][N];//换下了顺序 注意!
    pr pre[(1<<10)+1][N];
    bool inq[N],vis[N];
    std::queue<int> q;
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    void SPFA(int *f,pr *pre,int s)
    {
    	while(!q.empty())
    	{
    		int now=q.front();
    		q.pop(), inq[now]=0;
    		for(int x=now/m,y=now%m,xn,yn,nxt,i=0; i<4; ++i)
    			if((xn=x+to[i])>=0&&xn<n&&(yn=y+to[i+1])>=0&&yn<m && f[nxt=En(xn,yn)]>f[now]+cost[nxt])
    			{
    				f[nxt]=f[now]+cost[nxt], pre[nxt]=mp(now,s);
    				if(!inq[nxt]) inq[nxt]=1, q.push(nxt);
    			}
    	}
    }
    void DFS(int p,int s)
    {
    	if(!pre[s][p].second) return;//pre.second即 无转移了 
    	vis[p]=1;
    	if(pre[s][p].first==p) DFS(p,s^pre[s][p].second);
    	DFS(pre[s][p].first,pre[s][p].second);
    }
    
    int main()
    {
    	n=read(), m=read(); int K=0, rt=0;
    	memset(f,0x3f,sizeof f);
    	for(int tot=0,i=0; i<n; ++i)
    		for(int j=0; j<m; ++j,++tot)
    		{
    			cost[tot]=read();
    			if(!cost[tot]) f[1<<(K++)][tot]=0, rt=tot;
    		}
    	for(int s=1; s<(1<<K); ++s)
    	{
    		for(int i=0; i<n*m; ++i)
    		{
    			for(int sub=(s-1)&s; sub; sub=(sub-1)&s)
    				if(f[s][i]>f[sub][i]+f[sub^s][i]-cost[i])
    					f[s][i]=f[sub][i]+f[sub^s][i]-cost[i], pre[s][i]=mp(i,sub);
    			if(f[s][i]<INF) q.push(i), inq[i]=1;//多起点,inq[]还是不能省啊 
    		}
    		SPFA(f[s],pre[s],s);
    	}
    	printf("%d
    ",f[(1<<K)-1][rt]);
    	DFS(rt,(1<<K)-1);
    	for(int i=0,tot=0; i<n; ++i,putchar('
    '))
    		for(int j=0; j<m; ++j,++tot)
    			if(!cost[tot]) putchar('x');
    			else putchar(vis[tot]?'o':'_');
    
    	return 0;
    }
    
    
  • 相关阅读:
    通过代码获取log4net的文件路径
    关键字后面不加空格
    ASP.NET MVC 4 Content Map
    ASP.NET Overview
    ASP.NET 4 and Visual Studio 2010
    Newtonsoft.Json
    ASP.NET MVC
    log4net
    AutoMapper introduction
    ajax jsonp跨域
  • 原文地址:https://www.cnblogs.com/SovietPower/p/9333152.html
Copyright © 2020-2023  润新知