• Loj#2460「POI2010」桥Bridges【网络流,欧拉回路】


    正题

    题目链接:https://loj.ac/p/2460


    题目大意

    给出\(n\)个点\(m\)条边的一张无向图,每条边双向的权值不同,求一条经过的最大权值最小的欧拉回路。

    \(2\leq n\leq 1000,1\leq m\leq 20000\)


    解题思路

    显然我们可以二分答案,考虑二分答案后我们怎么做。

    不妨设权值小的那个方向为默认方向,那这样我们可以处理出一个入度-出度的数组\(deg\)

    此时我们翻转一条边可以让头的\(deg\)移动\(2\)到尾处,那么做法就很显然了。

    如果度数有奇数直接无解,否则我们用网络流连接能翻转的边去配平\(deg\)

    得到一个答案后我们就可以根据残余网络得到一张有欧拉回路的有向图了。

    然后就是有向图的欧拉回路,发现我们如果随机走一条路出去的话此时可能会走到死路。

    但是注意到如果一个位置是死路那么之前我们肯定来过至少一次这里。那么做法就是我们正常的遍历没有走过的边,到死路时我们回退回去,类似于正常的\(dfs\)可以继续搜其他的路。

    此时我们就可以合并我们伸出的两条路,反过来记录和输出遍历的边就好了


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<stack>
    #define mp(x,y) make_pair(x,y)
    using namespace std;
    const int M=2e4+1e3,N=1e3+10;
    int n,m,s,t,tot,S,fa[N],p[M];
    int deg[N],ls[N],dep[N];
    queue<int> q;stack<int> st;
    queue<pair<int,int> >G[N];
    struct edge{
    	int to,next,w;
    }a[M*4];
    struct node{
    	int x,y,w;
    }e[M];
    void addl(int x,int y,int w){
    	a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;
    	a[++tot].to=x;a[tot].next=ls[y];ls[y]=tot;a[tot].w=0;
    	return;
    }
    bool bfs(){
    	while(!q.empty())q.pop();q.push(s);
    	memset(dep,0,sizeof(dep));dep[s]=1;
    	while(!q.empty()){
    		int x=q.front();q.pop();
    		for(int i=ls[x];i;i=a[i].next){
    			int y=a[i].to;
    			if(!a[i].w||dep[y])continue;
    			dep[y]=dep[x]+1;
    			if(y==t)return 1;
    			q.push(y);
    		}
    	}
    	return 0;
    }
    int dinic(int x,int flow){
    	if(x==t)return flow;
    	int rest=0,k;
    	for(int i=ls[x];i;i=a[i].next){
    		int y=a[i].to;
    		if(dep[x]+1==dep[y]&&a[i].w){
    			rest+=(k=dinic(y,min(a[i].w,flow-rest)));
    			a[i].w-=k;a[i^1].w+=k;
    			if(rest==flow)return rest;
    		}
    	}
    	if(!rest)dep[x]=0;
    	return rest;
    }
    int find(int x)
    {return (fa[x]==x)?x:(fa[x]=find(fa[x]));}
    int check(int w){
    	memset(ls,0,sizeof(ls));tot=1;
    	for(int i=1;i<=n;i++){
    		if(deg[i]>0)addl(s,i,deg[i]/2);
    		if(deg[i]<0)addl(i,t,-deg[i]/2);
    	}
    	for(int i=1;i<=m;i++)
    		if(e[i].w<=w)
    			addl(e[i].x,e[i].y,1),p[i]=tot;
    	int ans=0;
    	while(bfs())
    		ans+=dinic(s,1e9);
    	return (ans==S);
    }
    void dfs(int x){
    	while(!G[x].empty()){
    		int y=G[x].front().first;
    		int k=G[x].front().second;
    		G[x].pop();dfs(y);
    		st.push(k);
    	}
    	return;
    }
    int main()
    {
    //	freopen("euler.in","r",stdin);
    //	freopen("euler.out","w",stdout);
    	scanf("%d%d",&n,&m);s=n+1;t=n+2;
    	for(int i=1;i<=n;i++)fa[i]=i;
    	int k=n,L=0,R=0;
    	for(int i=1;i<=m;i++){
    		int x,y,v1,v2;
    		scanf("%d%d%d%d",&x,&y,&v1,&v2);
    		if(v1>v2)swap(x,y),swap(v1,v2);
    		if(find(x)!=find(y))
    			fa[find(x)]=find(y),k--;
    		L=max(L,v1);R=max(R,v2);
    		e[i]=(node){y,x,v2};
    		deg[x]--;deg[y]++;
    	}
    	if(k!=1)return puts("NIE")&0;
    	for(int i=1;i<=n;i++)
    		if(deg[i]&1)return puts("NIE")&0;
    		else if(deg[i]>0) S+=deg[i]/2;
    	int lim=R;
    	while(L<=R){
    		int mid=(L+R)>>1;
    		if(check(mid))R=mid-1;
    		else L=mid+1;
    	}
    	if(L>lim)return puts("NIE")&0;
    	check(L);
    	printf("%d\n",L);
    	for(int i=1;i<=m;i++){
    		if(e[i].w<=L){
    			if(a[p[i]].w)G[e[i].x].push(mp(e[i].y,i));
    			else G[e[i].y].push(mp(e[i].x,i));
    		}
    		else G[e[i].y].push(mp(e[i].x,i));
    	}
    	dfs(1);
    	while(!st.empty())
    		printf("%d ",st.top()),st.pop();
    	return 0;
    }
    
  • 相关阅读:
    HL7数据类型分析(1)
    HL7 CDA高级培训
    JAVA开发常用类(二)
    Oracle中函数返回数据集
    Delphi中DLL或Package窗体载入与显示
    在乐山交流医疗保险审核工作
    JAVA开发常用类(一)
    JAVA开发常用类(三)
    Oracle函数返回超长字符串问题
    基于ODP的数据层基类C#源码
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15869219.html
Copyright © 2020-2023  润新知