Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 9114 | Accepted: 3841 |
Description
Input
Output
Sample Input
4 5 8 2 1 0 1 3 0 4 1 1 1 5 0 5 4 1 3 4 0 4 2 1 2 2 0 4 4 1 2 1 2 3 0 3 4 0 1 4 1 3 3 1 2 0 2 3 0 3 2 0 3 4 1 2 0 2 3 1 1 2 0 3 2 0
Sample Output
possible impossible impossible possible
题意:给一个既有有向边又有无向边的图,问这个图是否存在欧拉回路。
欧拉回路就是从一个点出发,经过每条边一次并回到起点的路径。这个只问是否存在,不用找路径。对于有向图,要求每个点的入度==出度就有欧拉回路,关于欧拉回路:点击打开链接
做法:要使有欧拉回路,就要使每个点的入度==出度,方法是网络流,首先是将无向边任意定向,计算每个点的入度和出度,如果有某个点的出入度之差为奇数,就肯定不存在欧拉回路,为偶数可以改变与这个点连接的无向边的方向来改变出入度(有向边反向点的出度+1,入度-1或者出度-1,入度+1)。这是一个判断条件。
然后就是关于网络流建模,建立源点s,汇点t。有向边方向已定,不能改变点的出入度,计算了关于有向边的点的出入度后,删掉。我们只关心无向边的方向,所以只在图中保存随意定向的无向边,流量为1。对于出度>入度的点i,它需要入度,建边(s,i,(out[i]-in[i])/2),出度<入度,需要出度,建边(i,t,(in[i]-out[i])/2)。然后跑一次最大流,如果能满流就是存在欧拉回路的,否则不存在。
关于正确性,每一次可行流必然是由s出发,经过一个出>入的点,最后经过一个入>出的点到t,将这个可行流上的边都反向,是不是对于出>入的点出度-1,入度+1,入>出的点出度+1,入度-1,这里不考虑s和t连的边对点的出入度影响,因为这只是为了求解最大流新建的边,不是原图中的边,对原图中的点的出入度没有影响。
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <queue>
using namespace std;
const int N = 300+10;
const int INF = 1e8;
struct node{
int v,flow,next;
node(){}
node(int v,int flow,int next):
v(v),flow(flow),next(next){}
}E[N*100];
int n,m,top,s,t;
int head[N]; ///邻接表头结点
int in[N]; ///入度
int out[N]; ///出度
int d[N]; ///dinic的dis
bool vis[N]; ///dinic用
void Init()
{
top = 0;
for(int i = 0;i < N;i++){
head[i] = -1;
in[i] = out[i] = 0;
}
}
void add(int u,int v,int flow)
{
E[top] = node(v,flow,head[u]);
head[u] = top++;
E[top] = node(u,0,head[v]);
head[v] = top++;
}
bool bfs()
{
memset(vis,false,sizeof vis);
memset(d,0,sizeof d);
queue<int>q;
vis[s] = true;
q.push(s);
while(!q.empty()){
int u = q.front();
q.pop();
for(int i = head[u];i != -1;i = E[i].next){
int v = E[i].v;
if(vis[v] || E[i].flow == 0) continue;
d[v] = d[u]+1;
vis[v] = true;
q.push(v);
}
}
return vis[t];
}
int dfs(int u,int a)
{
if(u == t || a == 0){
return a;
}
int flow = 0;
for(int i = head[u];i != -1;i = E[i].next){
int v = E[i].v;
if(d[v] != d[u]+1 || E[i].flow == 0) continue;
int f = dfs(v,min(a,E[i].flow));
if(f > 0){
a -= f;
E[i].flow -= f;
E[i^1].flow += f;
flow += f;
}
}
return flow;
}
int dinic() ///小红书最大流模板
{
int flow = 0;
while(bfs()){
flow += dfs(s,INF);
}
return flow;
}
int main(void)
{
int T;
scanf("%d",&T);
while(T--){
Init();
s = 210,t = 211;
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
out[u]++;
in[v]++;
if(w == 0){ ///只建无向向边的图,定向u->v
add(u,v,1);
}
}
bool flag = false;
for(int i = 1;i <= n;i++){
if(abs(in[i]-out[i]%2 == 1)) ///出入度差为奇数
flag = true;
else{
if(in[i] > out[i])
add(i,t,(in[i]-out[i])/2);
if((in[i] < out[i]))
add(s,i,(out[i]-in[i])/2);
}
}
if(flag){
printf("impossible\n");
continue;
}
int tot = 0; ///满流总量
for(int i = head[s];i != -1;i = E[i].next){
tot += E[i].flow;
}
int ans = dinic();
if(ans == tot)
printf("possible\n");
else
printf("impossible\n");
}
return 0;
}