• LG 4294[WC2008]游览计划(最小斯坦纳树)


    LG 4294[WC2008]游览计划

    前言

    我就喜欢这种重题的题,不知道是WC搬的CF还是CF搬的WC,不管了,反正一样就是了。

    既可以水 (2) 篇题解,也可以搞掉一黑一紫,何乐而不为?

    解题思路

    (k) 很小,又是连通性问题,显然最小斯坦纳树,问题是如何建图?如何求出方案?

    建图

    我们可以按四联通来建图,不用管边权。由于在这张图里只有点权,我们的斯坦纳树 DP 转移方程会有所改变(不要重复计算点权),具体看代码,其实很好理解(只要你理解了斯坦纳树的板子)。

    求出方案

    这个有点难度。其实可以和大部分 DP 一样,需要记录一下转移决策点。SPFA 可以这样,三角形不等式的部分怎么办?

    我们其实可以这样,如果可以已通过 SPFA 记录的决策点一直往前推,就往前,直到结束为止,我们就在把当前状态的递推在跑一遍即可。

    //Don't act like a loser.
    //This code is written by huayucaiji
    //You can only use the code for studying or finding mistakes
    //Or,you'll be punished by Sakyamuni!!!
    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    
    int read() {
    	char ch=getchar();
    	int f=1,x=0;
    	while(ch<'0'||ch>'9') {
    		if(ch=='-')
    			f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') {
    		x=x*10+ch-'0';
    		ch=getchar();
    	}
    	return f*x;
    }
    
    const int MAXN=110,MAXK=11,INF=2e9;
    const int dx[]={1,0,-1,0};
    const int dy[]={0,1,0,-1};
    
    int n,m,k,cnt;
    int a[MAXN],h[MAXN],f[MAXN][1<<MAXK],g[MAXN][1<<MAXK];
    char ans[MAXN];
    bool inq[MAXN],vis[MAXN][1<<MAXK];
    queue<int> q;
    
    int idx(int i,int j) {
    	return (i-1)*m+j;
    } 
    bool ok(int i,int j) {
    	if(i>n||i<1||j>m||j<1) {
    		return 0;
    	}
    	return 1;
    }
    
    struct edge {
    	int to,nxt;
    }e[10000];
    
    void addedge(int u,int v) {
    	e[++cnt].nxt=h[u];
    	e[cnt].to=v;
    	h[u]=cnt;
    }
    
    void SPFA(int s) {
    	while(!q.empty()) {
    		int u=q.front();
    		q.pop();
    		inq[u]=0;
    		
    		for(int i=h[u];i;i=e[i].nxt) {
    			int v=e[i].to;
    			if(f[v][s]>f[u][s]+a[v]) {
    				f[v][s]=f[u][s]+a[v];
    				g[v][s]=u;
    				if(!inq[v]) {
    					q.push(v);
    					inq[v]=1;
    				}
    			}
    		}
    	}
    }
    
    void getans(int u,int s) {
    	if(vis[u][s]) {
    		return ;
    	}
    	vis[u][s]=1;
    	
    	ans[u]='o';
        //一直往前推
    	while(g[u][s]!=-1&&f[g[u][s]][s]+a[u]==f[u][s]) {
    		u=g[u][s];
    		getans(u,s);
    	}
    	for(int t=s&(s-1);t;t=(t-1)&s) {
    		if(f[u][t]+f[u][s^t]-a[u]==f[u][s]) {
    			getans(u,t);
    			getans(u,s^t);
    			break;
                //重新递推
    		}
    	}
    }
    
    signed main() {
    	cin>>n>>m;
    	for(int i=1;i<=n*m;i++) {
    		for(int j=0;j<=1023;j++) {
    			f[i][j]=INF;
    			g[i][j]=-1;
    		}
    	}
    	for(int i=1;i<=n;i++) {
    		for(int j=1;j<=m;j++) {
    			ans[idx(i,j)]='_';
    			cin>>a[idx(i,j)];
    			if(!a[idx(i,j)]) {
    				f[idx(i,j)][1<<k]=0;
    				k++;
    			}
    			for(int p=0;p<4;p++) {
    				if(ok(i+dx[p],j+dy[p])) {
    					addedge(idx(i,j),idx(i+dx[p],j+dy[p]));
    				}
    			}
    		}
    	}
    	
    	int mx=(1<<k)-1;
    	
    	for(int s=0;s<=mx;s++) {
    		for(int i=1;i<=n*m;i++) {
    			for(int t=s&(s-1);t;t=(t-1)&s) {
    				f[i][s]=min(f[i][s],f[i][t]+f[i][s^t]-a[i]);
                    //同一点的点权不要重复算
    			}
    			if(f[i][s]<INF) {
    				inq[i]=1;
    				q.push(i);
    			}
    		}
    		SPFA(s);
    	}
    	
    	int minn=INF,mark=0;
    	for(int i=1;i<=n*m;i++) {
    		if(f[i][mx]<minn) {
    			minn=f[i][mx];
    			mark=i;
    		}
    	}
    	
    	cout<<minn<<endl;
    	getans(mark,mx);
    	for(int i=1;i<=n;i++) {
    		for(int j=1;j<=m;j++) {
    			if(!a[idx(i,j)]) {
    				ans[idx(i,j)]='x';
    			}
    			printf("%c",ans[idx(i,j)]);
    		}
    		puts("");
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    信息量
    MVC4的实战:排球计分(一)(综述)
    排球计分规则3.17
    观后感-----怎样成为一个高手
    本学期最后一个博客
    第五组作业
    个人作业
    第五组作业
    个人作业
    一周的总结
  • 原文地址:https://www.cnblogs.com/huayucaiji/p/LG4294.html
Copyright © 2020-2023  润新知