YYD为了减肥,他来到了瘦海,这是一个巨大的海,海中有n个小岛,小岛之间有m座桥连接,两个小岛之间不会有两座桥,并且从一个小岛可以到另外任意一个小岛。现在YYD想骑单车从小岛1出发,骑过每一座桥,到达每一个小岛,然后回到小岛1。霸中同学为了让YYD减肥成功,召唤了大风,由于是海上,风变得十分大,经过每一座桥都有不可避免的风阻碍YYD,YYD十分ddt,于是用泡芙贿赂了你,希望你能帮他找出一条承受的最大风力最小的路线。
YYD为了减肥,他来到了瘦海,这是一个巨大的海,海中有n个小岛,小岛之间有m座桥连接,两个小岛之间不会有两座桥,并且从一个小岛可以到另外任意一个小岛。现在YYD想骑单车从小岛1出发,骑过每一座桥,到达每一个小岛,然后回到小岛1。霸中同学为了让YYD减肥成功,召唤了大风,由于是海上,风变得十分大,经过每一座桥都有不可避免的风阻碍YYD,YYD十分ddt,于是用泡芙贿赂了你,希望你能帮他找出一条承受的最大风力最小的路线。
输入:第一行为两个用空格隔开的整数n(2<=n<=1000),m(1<=m<=2000),接下来读入m行由空格隔开的4个整数a,b(1<=a,b<=n,a<>b),c,d(1<=c,d<=1000),表示第i+1行第i座桥连接小岛a和b,从a到b承受的风力为c,从b到a承受的风力为d。
输出:如果无法完成减肥计划,则输出NIE,否则第一行输出承受风力的最大值(要使它最小)
by poi
最大流的应用混合图判断欧拉回路+二分答案
每次二分一个值x,把权值大于等于x的边全部去掉。如果一座桥只被去掉一条边就把它视作有向边,如果两条边都没去掉就把它视作无向边。那么问题就转化为判断混合图(有向边和无向边均存在)是否存在欧拉回路。
·无向图和有向图存在欧拉回路的充要条件:
①无向图存在欧拉回路,当且仅当该图所有顶点度数都为偶数且该图连通。
②有向图存在欧拉回路,当且仅当该图所有顶点入度等于出度且该图连通。
先把无向边随机确定一个方向,然后计算每个点的入度和出度,只有在每个点入度与出度的差都为偶数时在可能存在欧拉回路。(想想如果有奇数为什么不可能存在欧拉回路?)
好了,现在图中所有节点入度和出度的差都为偶数了,我们不妨设这个偶数为2x。
现在问题就变成了:修改某些边的方向,让每个点的入度等于出度。这里就要用到最大流模型。首先,有向边不能改变方向,所以在构图是就只添加无向边。那无向图如何构图呢?就按照一开始随机的方向的反方向构图(为什么反方向呢?画个图就明白了),每条边容量1。另外新建源点s、汇点t。对于入度>出度的点u,从s到u连边,容量为x;对于出度>入度的点v,从v到t连边,容量为x(x的含义已经在上面给出说明)。之后,跑一次最大流,如果满流就有欧拉回路,反之就没有欧拉回路。(查看流量分配,把流量为1的边反向,得到的图就满足所有点的入度=出度。想想为什么?)
混合图的欧拉回路问题就这样解决了。
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define F(i,j,n) for(int i=j;i<=n;i++) #define D(i,j,n) for(int i=j;i>=n;i--) #define LL long long #define pa pair<int,int> #define MAXN 10005 #define MAXM 40005 #define INF 1000000000 using namespace std; int n,m,s,t,l,r,cnt,ans; int dis[MAXN],head[MAXN],cur[MAXN],in[MAXN],out[MAXN],u[MAXM],v[MAXM],w1[MAXM],w2[MAXM]; struct edge_type { int next,to,v; }e[MAXM]; inline int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void add_edge(int x,int y,int v) { e[++cnt]=(edge_type){head[x],y,v};head[x]=cnt; e[++cnt]=(edge_type){head[y],x,0};head[y]=cnt; } inline bool bfs() { queue<int>q; memset(dis,-1,sizeof(dis)); dis[s]=0;q.push(s); while (!q.empty()) { int tmp=q.front();q.pop(); if (tmp==t) return true; for(int i=head[tmp];i;i=e[i].next) if (e[i].v&&dis[e[i].to]==-1) { dis[e[i].to]=dis[tmp]+1; q.push(e[i].to); } } return false; } inline int dfs(int x,int f) { if (x==t) return f; int tmp,sum=0; for(int &i=cur[x];i;i=e[i].next) { int y=e[i].to; if (e[i].v&&dis[y]==dis[x]+1) { tmp=dfs(y,min(f-sum,e[i].v)); e[i].v-=tmp;e[i^1].v+=tmp;sum+=tmp; if (sum==f) return sum; } } if (!sum) dis[x]=-1; return sum; } inline void dinic() { ans=0; while (bfs()) { F(i,1,t) cur[i]=head[i]; ans+=dfs(s,INF); } }//最大流模板 inline bool check(int mid) { int sum=0; cnt=1; memset(head,0,sizeof(head)); memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); F(i,1,m) { if (w1[i]<=mid) out[u[i]]++,in[v[i]]++;//计算入度和出度 if (w2[i]<=mid) add_edge(v[i],u[i],1);//无向边加入图中,方向为假设方向的反方向 } F(i,1,n) if (abs(in[i]-out[i])&1) return false;//如果入度与出度差为奇数,则不可能存在欧拉回路 F(i,1,n) { int x=(in[i]-out[i])/2;//入度与出度的差除以2 sum+=x>0?x:0; if (x>0) add_edge(s,i,x);//入度大于出度就从源点向该点连边 if (x<0) add_edge(i,t,-x);//入度小于出度就从该点向汇点连边 } dinic(); return ans==sum;//判断是否满流 }//重点理解这个函数 int main() { n=read();m=read(); l=INF;r=-INF;s=n+1;t=n+2; F(i,1,m) { u[i]=read();v[i]=read();w1[i]=read();w2[i]=read(); if (w1[i]>w2[i]) swap(w1[i],w2[i]),swap(u[i],v[i]); l=min(l,w1[i]);r=max(r,w2[i]); } while (l<r) { int mid=(l+r)>>1; if (check(mid)) r=mid;else l=mid+1; } if (!check(l)) printf("NIE\n");else printf("%d\n",l); return 0; }