zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
  • 相关阅读:
    codeforces C. Cows and Sequence 解题报告
    codeforces A. Point on Spiral 解题报告
    codeforces C. New Year Ratings Change 解题报告
    codeforces A. Fox and Box Accumulation 解题报告
    codeforces B. Multitasking 解题报告
    git命令使用
    shell简单使用
    知识束缚
    php 调用系统命令
    数据传输方式(前端与后台 ,后台与后台)
  • 原文地址:https://www.cnblogs.com/huayucaiji/p/LG4294.html
Copyright © 2011-2022 走看看