反正对于现在的我来说是好题。顺便膜po大犇和dingchao大犇。
网络流什么的还是再开一个专题好了。
欧拉回路问题参考论文《欧拉回路性质与应用探究》by 仇荣琦。
POI2010 题解整理
Description
小C为了减肥,他来到了瘦海,这是一个巨大的海,海中有n个小岛,小岛之间有m座桥连接,两个小岛之间不会有两座桥,并且从一个小岛可以到另外任意一个小岛。
现在小C想骑单车从小岛1出发,骑过每一座桥,到达每一个小岛,然后回到小岛1。小Q为了让小C减肥成功,召唤了大风,由于是海上,风变得十分大,经过每一座桥都有不可避免的风阻碍小C。小C十分ddt,于是用泡芙贿赂了你,希望你能帮他找出一条承受的最大风力最小的路线。
注意的是每条边能且只能经过一次,即欧拉回路。
Input
- Line 1:两个为空格隔开的整数 n , m 。
- Line 2~m+1:每行由空格隔开的4个整数 a , b , c , d 组成,表示第 i+1 行第 i 座桥连接小岛 a 和 b ,从 a 到 b 承受的风力为 c ,从 b 到 a 承受的风力为 d 。
- 【数据范围】:
- 1≤m≤2000,2≤n≤1000 。
- 1≤a≠b≤n,1≤c,d≤1000 。
Sample Input
4 4
1 2 2 4
2 3 3 4
3 4 4 4
4 1 5 4Output
如果无法完成减肥计划,则输出NIE;
否则第一行输出最小的承受风力的最大值,且第二行输出一份方案(输出的是边的编号,第 i+1 行对应编号 i )。
Sample Output
4
4 3 2 1Output Details
样例如图所示:
首先这一题套在外面的二分答案是非常明显的,怎么说题目中也频繁出现了“最大值最小”之类的字眼。二分枚举这个最大风力,那么我们可以把剩下的图提取出来。
根据题意要求:每条边必须经过一次,而点可以多次经过,并且最后要回到原点。这样的定义指向欧拉回路。于是本题的任务变成:
图论欧拉回路初步:
引入:(我这引入也真TM有个性)
NOIP初赛有这样一道题目:
[NOIP2007 提高组初赛 T9]欧拉图 G 是指可以构成一个闭回路的图,且图 G 的每一条边恰好在这个闭回路上出现一次(即一笔画成)。在以下各个描述中,不一定是欧拉图的是(D)。
- A. 图 G 中没有度为奇数的顶点 。
- B. 包含欧拉环游的图。
- C. 包含欧拉闭迹的图。
- D. 存在一条回路,通过每个顶点恰好一次。
- E. 本身为闭迹的图。
欧拉回路的基本概念(顺便解决上述问题):设图 G=(V,E) 。
注意D选项是错误的。(哈密顿图:通过图 G 的每个结点有且只有一次的回路,就是哈密顿回路,存在哈密顿回路的图就是哈密顿图)
欧拉图的判定定理:
欧拉图的性质:
1)求欧拉回路
根据上述性质,得到求欧拉回路的算法(注意是在欧拉图上):
void Euler u //伪代码
while(next v)
if e(u,v) unmarked
mark e(u,v),e(v,u)
Euler v
stack.push e(u,v)
上述伪代码的时间复杂度 O(E) ,由于边数过多会有爆栈危险,我们也会采用非递归形式。(待添加)
2)混合路欧拉路径判定
对每条无向边进行随机定向。
通过随机定边,我们暂时构造出了一个有向图,根据上述欧拉回路的有向图判定定理,我们在接下来的处理中只要通过调整该有向图中由无向边变成的有向边的方向,使得所有节点的入度=出度即可。
对当前的新图构建网络。
我们按照当前点的出入度进行建边:
假设该点的 degree=u+−u− , degree>0 表示需要更改从它连出去的 degree 条边,同理 degree<0 表示需要更改从它连出去的 −degree 条边。
再定义 e(u,v,1) 表示有值为1的流从 u 向 v 流出,此时 edge(u,v) 被反向。同理,我们对于一个 degree>0 的点,为了让其 degree 减小,我们假设一个虚拟源点 S ,此时若 e(S,u,degree2) 中的流量能全部跑光,则说明 u 后面的边可以通过修改方向,使得 u 的出度=入度。
那么我们对按照以下方式建边:
跑最大流算法,检查是否满流。(最大流初步)(待补充)
检查是否满流时,按照上述定义只需要判断所有与S相连的边上是否满流即可。
Code :(请无视我因为取名癌把我男神名字丢结构体名称的举动)
#include
#define M 2005
#define S 0
#define T 2004
#define inf 0x3f3f3f3f
using namespace std;
int n,m;
struct edge{int u,v,w1,w2,id;}Edges[M];
struct Union_Find_Set{
int fa[M],cnt;
void Init(){
for(int i=1;i<=n;i++)fa[i]=i;
cnt=n;
}
int Getfa(int x){
return (fa[x]==x)?fa[x]:fa[x]=Getfa(fa[x]);
}
void Union(int u,int v){
u=Getfa(u),v=Getfa(v);
if(u!=v)fa[u]=v,cnt--;
}
}Emiya;//判断重构图是否为连通图
struct Max_Flow{
struct node{int v,f,nxt,id;}Nodes[M*M];
int head[M],tot,degree[M],level[M];
void Init(){
memset(head,-1,sizeof(head));
memset(degree,0,sizeof(degree));
tot=1;
}
void Add_edge(int u,int v,int w,int id){
Nodes[++tot]=(node){v,w,head[u],id};head[u]=tot;
Nodes[++tot]=(node){u,0,head[v],id};head[v]=tot;
}//邻接表
bool bfs(){
memset(level,-1,sizeof(level));
level[S]=1;
queue<int>Q;Q.push(S);
while(!Q.empty()){
int u=Q.front();Q.pop();
for(int j=head[u];~j;j=Nodes[j].nxt)
if(Nodes[j].f&&!~level[Nodes[j].v]){
level[Nodes[j].v]=level[u]+1;
Q.push(Nodes[j].v);
if(Nodes[j].v==T)return true;
}
}
return false;
}
int Dinic(int u,int flow){
if(u==T)return flow;
int left=flow;
for(int j=head[u];~j&&left;j=Nodes[j].nxt)
if(Nodes[j].f&&level[u]+1==level[Nodes[j].v]){
int tmp=Dinic(Nodes[j].v,min(left,Nodes[j].f));
if(tmp){
left-=tmp;
Nodes[j].f-=tmp;
Nodes[j^1].f+=tmp;
}
}
if(left)level[u]=-1;
return flow-left;
}//最大流Dinic算法,Ford_Fulkerson或Edmonds_Karp算法亦可以套用
bool judge(int val){
Emiya.Init();Init();
for(int i=1;i<=m;i++){
int u=Edges[i].u,v=Edges[i].v;
if(Edges[i].w2<=val){//degree_in +1 degree_out -1
Emiya.Union(u,v);
Add_edge(u,v,1,Edges[i].id);
degree[u]++;
degree[v]--;
}else if(Edges[i].w1<=val){
Emiya.Union(u,v);
degree[u]--;
degree[v]++;
}
}
if(Emiya.cnt>1)return false;//原图不连通
for(int i=1;i<=n;i++){
if(degree[i]&1)return false;//不满足欧拉回路性质
if(degree[i]>0)Add_edge(S,i,degree[i]>>1,S);
else if(degree[i]<0)Add_edge(i,T,-degree[i]>>1,T);
}
while(bfs())Dinic(S,inf);//使劲跑网络流,如果构成满流则说明找到了欧拉回路
for(int j=head[S];~j;j=Nodes[j].nxt)if(Nodes[j].f)return false;
return true;
}
}Shirou;
int bisection(int R){
int L=1,res=-1;
while(L<=R){
int mid=L+R>>1;
if(Shirou.judge(mid)){//跑欧拉回路
R=mid-1;
res=mid;
}else L=mid+1;
}return res;
}
int head[M],tot=0;
struct New_Graph{int v,nxt,id;bool vis;}Graph[M<<1];
void Add_New_Edge(int u,int v,int id){
Graph[++tot]=(New_Graph){v,head[u],id,0};head[u]=tot;
}
bool f=false;
stack<int>stk;
void Euler_Print(int u){
for(int j=head[u];~j;j=Graph[j].nxt){
if(!Graph[j].vis){
Graph[j].vis=true;
Euler_Print(Graph[j].v);
stk.push(Graph[j].id);
}
}
}//打印解部分
int main(){
int num=0;
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w1,w2;
scanf("%d %d %d %d",&u,&v,&w1,&w2);
if(w1>w2)swap(u,v),swap(w1,w2);
Edges[i]=(edge){u,v,w1,w2,i};
if(numif(numint ans=bisection(num);//二分枚举当前的最大风力
if(!~ans)puts("NIE");
else{
printf("%d\n",ans);
Shirou.judge(ans);
memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++)//没有扔进网络流的单向边
if(Edges[i].w2>ans&&Edges[i].w1<=ans)
Add_New_Edge(Edges[i].u,Edges[i].v,Edges[i].id);
for(int u=1;u<=n;u++)//网络流跑出来的满流边
for(int j=Shirou.head[u];~j;j=Shirou.Nodes[j].nxt){
Max_Flow::node now=Shirou.Nodes[j];
if(!now.f&&now.v!=S&&now.v!=T)Add_New_Edge(u,now.v,now.id);
}
Euler_Print(1);
while(!stk.empty()){
printf("%d%c",stk.top(),stk.size()==1?'\n':' ');
stk.pop();
}
puts("");
}
return 0;
}