• 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;
    }
    
  • 相关阅读:
    最佳的思维导图生成工具——markmap 使用教程
    07. struts2中对Action的管理方式
    06. struts2中指定struts2处理的请求后缀
    05. struts2中为Action属性注入值
    03. struts2中Action配置的各项默认值
    04. struts2中Result配置的各种视图转发类型
    02. struts2中Action名称的搜索顺序
    NSAttributedString使用CSS+html创建换行符无效( 无效)处理方法
    UITextView 添加到UITableViewCell上使用AttributedString点击链接不调用代理方法的处理方法及自定义link样式需要注意的问题
    iOS TableView reaload delete的实际操作
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15869219.html
Copyright © 2020-2023  润新知