(有任何问题欢迎留言或私聊
例题:POJ-1637-Sightseeing tour
题意:
翻译过来就是:
1. 有n个点,m条边;边用u,v,w描述,w==1表示有向边,w==0表示无向边。
2. 判断途中是否存在欧拉回路(一条路径不重复地经过所有的边一次)。
思路:
1. 不论无向边的方向怎么指定,若存在欧拉回路,每个点出度和入度的差一定为偶数。因为有向图欧拉回路每个点的出度等于入度,反向一条边后,度数差变换2。
2. 因为不知道怎么指定无向边的方向,这时可以利用到最大流的调整流量的特性。给每条无向边随意定一个方向,建边u->v,流量为1。这条边可能被最大流过程调整。
3. 建图过程:这里的度数差 = 出度-入度
遍历每个点,若度数差为奇数,则不存在欧拉回路,break;
若度数为正数,则源点向节点连边,流量为度数差/2;
若度数为负数,则节点向汇点连边,流量为度数差的绝对值/2。
4. 为什么流量要等于度数差的一半呢?
想想,这个图已经被我们变成有向图了是吧。你知道有向图的欧拉回路每个点的入度等于出度。举个栗子,有一个点的出度比入度多4,这时是不是只需要且只能改变两条边的方向,入度才能等于出度?显然是这样的。所以源点和这个节点连边,流量设为度数差/2,表示你可以通过网络流调整某些边(数量为度数差/2)的方向让它出度等于入度,而且必须调整。
5.最大流跑完了又要怎么判断是否存在欧拉回路呢?
答案是,遍历每条源点连出的边,判断是否满流。必须所有的边都满流才存在欧拉回路。因为只要有一条边不是满流,就表示那个节点的入度不等于出度,这点前面讲过了。
6. 至此流程就跑完了,建议画个图辅助一下理解,不管画的好不好看,自己看得懂就行。
相关类型题要注意的要点:
1. 连通性;(依题而定,方法有:dfs,并查集等)
2. 出度与入度的关系,奇数度的数量;(分有向图,无向图,混合图)
3.dfs的起点选择让我很难受
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MX=205;
int s,t,e,fr[MX],h[MX],nh[MX],du[MX];
LL ans;
int n,m;
const LL inf=1e17;
struct lp
{
int t,ne;
LL w;
}a[MX*2];
inline int ab(int x){
return x<0?0-x:x;
}
void ins(int f,int t,LL w)
{
a[++e].t=t,a[e].ne=fr[f],fr[f]=e,a[e].w=w;
a[++e].t=f,a[e].ne=fr[t],fr[t]=e,a[e].w=0;
}
LL sap(int u,LL fl)
{
if (u==t) return fl;
LL res=fl;
for (int i=fr[u];i;i=a[i].ne){
if (a[i].w&&h[a[i].t]+1==h[u]){
LL t=sap(a[i].t,min(res,a[i].w));
a[i].w-=t,a[i^1].w+=t;
if (!(res-=t)) return fl;
}
}
if (!(--nh[h[u]])) h[t]=t;
++nh[++h[u]];
return fl-res;
}
void init(){
memset(fr,0,sizeof(fr));
memset(nh,0,sizeof(nh));
memset(h,0,sizeof(h));
memset(du,0,sizeof(du));
s=1,t=n+2;ans=0;e=1;
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
init();
for (int i=1,x,y,z;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
if(z==0) ins(x+1,y+1,1);
du[x]++;du[y]--;
}
int flag=0;
for(int i=1;i<=n;++i){
if(ab(du[i])%2!=0){
flag=1;
break;
}
if(du[i]>0){
ins(s,i+1,du[i]/2);
}else if(du[i]<0){
ins(i+1,t,-du[i]/2);
}
}
if(flag){
printf("impossible\n");
continue;
}
nh[0]=t;
while (h[t]!=t){
ans+=sap(s,inf);
}
for(int i=fr[s];i;i=a[i].ne){
if(i%2==0&&a[i].w!=0){
flag=1;
break;
}
}
if(flag)printf("impossible\n");
else printf("possible\n");
}
return 0;
}