zoukankan      html  css  js  c++  java
  • 最小路径覆盖

    最小路径覆盖

    路径覆盖

    路径覆盖指一个路径的集合中所有路径点集的并集为原图点集。最小路径覆盖使路径集合大小最小。

    路径覆盖分为可否重复选点两种。可重复选点在建模中加大流量即可。

    网络流模型

    • 建超级源 (S) 和超级汇 (T),将每个点拆点。
    • (S) 向所有点 (i) 连边,将所有点 (i')(T) 连边。
    • 对于原图边集,每一条边 (u)(v') 连边。
    • 选点不重的最小路径覆盖问题,上述所有边流量为 1,答案为点数减去最小割。

    考虑对于这个模型建出来的图求出的最小割是什么。它实际上是最大的可以合并的路径条数。因为每合并两条路径相当于总路径数就减少了,所以有最小路径覆盖等于点数减最大匹配

    其中最大匹配就是我们刚才的模型。可以发现,我们实际上就是建了一个二分图在求最大匹配。

    P2764 最小路径覆盖问题

    题意如上,要求构造方案。

    我的构造方式如下:利用并查集,对于每条道路的端点,若这条边被选择了就将这两条边加入一个并查集,每个并查集代表一条路径。最后从每个路径的起点进行一遍搜索即可。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cctype>
    #include<cstring>
    #include<cmath>
    using namespace std;
    inline int read(){
    	int w=0,x=0;char c=getchar();
    	while(!isdigit(c))w|=c=='-',c=getchar();
    	while(isdigit(c))x=x*10+(c^48),c=getchar();
    	return w?-x:x;
    }
    namespace star
    {
    	const int maxn=305,maxm=6305,S=303,T=304,INF=0x3f3f3f3f;
    	int n,m;
    	int ecnt=1,head[maxn],cur[maxn],to[maxm<<1],nxt[maxm<<1],w[maxm<<1];
    	inline void addedge(int a,int b,int c){
    		to[++ecnt]=b,nxt[ecnt]=head[a],head[a]=ecnt,w[ecnt]=c;
    		to[++ecnt]=a,nxt[ecnt]=head[b],head[b]=ecnt,w[ecnt]=0;
    	}
    	int dep[maxn];
    	inline bool bfs(){
    		memset(dep,-1,sizeof dep);
    		static int q[maxn];
    		int hd=0,tl=1;
    		q[tl]=S;dep[S]=0;
    		while(hd<tl){
    			int x=q[++hd];
    			for(int u,i=head[x];i;i=nxt[i]) if(dep[u=to[i]]==-1 and w[i])
    				dep[u]=dep[x]+1,q[++tl]=u;
    		}
    		return dep[T]!=-1;
    	}
    	int dfs(int x,int flow){
    		if(x==T) return flow;
    		int used=0,u;
    		for(int& i=cur[x];i;i=nxt[i]) if(dep[u=to[i]]==dep[x]+1 and w[i]){
    			int v=dfs(u,min(flow-used,w[i]));
    			used+=v,w[i]-=v,w[i^1]+=v;
    			if(used==flow)break;
    		}
    		return used;
    	}
    	inline int dinic(){
    		int ans=0;
    		while(bfs()) memcpy(cur,head,sizeof head),ans+=dfs(S,INF);
    		return ans;
    	}
    	int fa[maxn];
    	bool vis[maxn];
    	int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    	void Dfs(int x){printf("%d ",x);for(int u,i=head[x];i;i=nxt[i]) if(!w[i] and (u=to[i])>n and u<=n*2) Dfs(u-n);}
    	inline void work(){
    		n=read(),m=read();
    		for(int u,v,i=1;i<=m;i++) u=read(),v=read(),addedge(u,v+n,1);
    		for(int i=1;i<=n;i++) addedge(S,i,1),addedge(i+n,T,1),fa[i]=i;
    		int ans=n-dinic();
    		for(int i=1;i<=m;i++) if(!w[i<<1]) fa[find(to[i<<1]-n)]=find(to[i<<1|1]);
    		for(int i=1;i<=n;i++) if(find(i)==i) Dfs(i),puts("");
    		printf("%d
    ",ans);
    	}
    }
    signed main(){
    	star::work();
    	return 0;
    }
    

    P2469 星际竞速

    题意

    给你若干单向道路,并且每个点有一个可以从任意位置任意时刻到达该点的代价。求遍历所有点的最小代价。

    思路

    参照最小路径覆盖的思路,拆出来的第二个点与汇点间的连边表示到达该点。建模方式如下:

    • 对于所给的所有边 ((u,v)),将 (u)(v') 建边权费用边。
    • 从源点向所有 (i) 连无费用边。
    • 从所有 (i') 向汇点连无费用边。
    • 从源点向所有 (i') 连传送费用边。上述所有边流量为 1。(因为从任何位置传送与起点无关)

    跑最小费用最大流即为答案。正确性,考虑建出来的图,因为源点到所有点都有传送的边,所以跑最小割一定会经过所有点,并且因为都是单向边且流量为 1,所以保证正确。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cctype>
    #include<cstring>
    #include<cmath>
    #include<queue>
    using namespace std;
    inline int read(){
    	int w=0,x=0;char c=getchar();
    	while(!isdigit(c))w|=c=='-',c=getchar();
    	while(isdigit(c))x=x*10+(c^48),c=getchar();
    	return w?-x:x;
    }
    namespace star
    {
    	const int maxn=3005,maxm=600005,S=3003,T=3004,INF=0x3f3f3f3f;
    	int n,m;
    	int ecnt=1,head[maxn],to[maxm],nxt[maxm],w[maxm],v[maxm];
    	inline void addedge(int a,int b,int c,int d){
    		to[++ecnt]=b,nxt[ecnt]=head[a],head[a]=ecnt,w[ecnt]=c,v[ecnt]=d;
    		to[++ecnt]=a,nxt[ecnt]=head[b],head[b]=ecnt,w[ecnt]=0,v[ecnt]=-d;
    	}
    	int dis[maxn],pre[maxn];
    	bool vis[maxn];
    	inline bool spfa(){
    		queue<int> q;
    		memset(pre,0,sizeof pre);
    		memset(vis,0,sizeof vis);
    		memset(dis,INF,sizeof dis);
    		q.push(S);dis[S]=0;
    		while(!q.empty()){
    			int x=q.front();q.pop();
    			vis[x]=false;
    			for(int u,i=head[x];i;i=nxt[i]) if(dis[u=to[i]]>dis[x]+v[i] and w[i]){
    				dis[u]=dis[x]+v[i];pre[u]=i;
    				if(!vis[u]) vis[u]=true,q.push(u);
    			}
    		}
    		return pre[T];
    	}
    	int a[maxn];
    	inline void work(){
    		n=read(),m=read();
    		for(int i=1;i<=n;i++) addedge(S,i,1,0),addedge(i+n,T,1,0),addedge(S,i+n,1,a[i]=read());
    		for(int u,v,zp,i=1;i<=m;i++){
    			u=read(),v=read();
    			if(u>v) swap(u,v);
    			if((zp=read())<a[v])addedge(u,v+n,1,zp);
    		}
    		int ans=0;
    		while(spfa()){
    			int mn=INF;
    			for(int i=pre[T];i;i=pre[to[i^1]]) mn=min(mn,w[i]);
    			for(int i=pre[T];i;i=pre[to[i^1]]) w[i]-=mn,w[i^1]+=mn;
    			ans+=dis[T]*mn;
    		}
    		printf("%d
    ",ans);
    	}
    }
    signed main(){
    	star::work();
    	return 0;
    }
    
  • 相关阅读:
    django 项目需要注意的一些点
    VUE之路
    Oracle 表格碎片的查看方法
    RHEL 6.x or 7.x 使用分区绑定ASM 磁盘的方法
    RMAN 修复主库 nologging 操作导致物理备库的坏块
    Oracle 数据库19c 回退降级到 11.2.0.4 方案
    如何评估oracle 数据库rman全备和增量备份大小
    在将Oracle GI和DB升级到19c或降级到以前的版本之前需要应用的补丁 (Doc ID 2668071.1)
    Oracle 数据库坏块处理
    opatch auto 安装11.2.0.4.20190115 PSU遇到 OUI-67133: Execution of PRE script failed,with returen value 1 报错
  • 原文地址:https://www.cnblogs.com/BrotherHood/p/14294891.html
Copyright © 2011-2022 走看看