Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 9262 | Accepted: 3914 |
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
Source
解题思路:
非常好的一道题,自己没有想出来,参考了一下kuangbin的题解,不过感觉有的关于建图他好像有地方没有写清楚(也可能是我自己理解有问题),而且个人感觉他说的欧拉通路的扩展的解法中,随意确定的初始方向会造成在一些情况下即使存在欧拉通路也无法找到起点和点。
首先,我们对图中每条无向边随意确定一个方向,然后统计每个顶点的入度出度。可以发现如果改变一条边的方向,那么与它相连的顶点的出度减入度一定变化2。所以如果存在初始出度入度差值为奇数的点,那么这个点的差值一定不可能通过边的反向变为0(也就是出度等于入度),那么就一定不能构成欧拉图。
然后回想一下,网络流中增广路的增广过程就可以看作是把增广路上的边全部反向的过程,而且很容易发现对于增广路中间的结点,边反向的时候并没有改变它的差值。于是我们就可以考虑把差值作为流量,由于每次改变一定是2,所以我们应该把差值/2(为了方便就叫”cha值“了)作为流量。于是我们就可以开始建图了。
N个顶点不变,再设置一个源点一个汇点,如果这个结点的cha值为正,我们就连一条从原点到这个结点容量为cha值的边,如果为负,就连接一条从此结点到汇点容量为-cha值的边。然后把原图中的每个已随意确定方向的无向边添加到图中容量为1。最后我们对这个图跑最大流,检查与源点连接的每条边是否全部满流,如果是就说明这些点的cha值已经变为0,则这个图可以形成欧拉图。(因为出度入度的变化一定是对称的,所以当初始cha值为正的点差值为0时,初始为负的点一定为0)
这个问题还可以进行扩展:判断是否存在欧拉通路。只需要最开始的时候先判断一下,是否只有两个点的差值(不是cha值)为奇数,那么这两个一定又一个时起点一个是终点,就可以加一条连接着两条边的有向边(两种情况,都需要试一下),然后就转化成了刚才的欧拉图判读。
最后附一组自己认为可能会使kuangbin的扩展问题解法出问题的数据(默认无向边初始定向时第一个输入的结点指向第二个输入的结点)。
4 3
1 2 0
1 3 0
2 4 0
3 4 0
AC代码:
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
#define INF 0x3f3f3f3f
#define mem(a,b) memset((a),(b),sizeof(a))
const int MAXV=200+3;
struct Edge
{
int to,cap,rev;
Edge(int t, int c, int r):to(t), cap(c), rev(r){}
};
int N,M;
int in[MAXV],out[MAXV];//每个节点的入度,出度
vector<Edge> G[MAXV];
int level[MAXV];
int iter[MAXV];
void add_edge(int from, int to, int cap)
{
G[from].push_back(Edge(to, cap, G[to].size()));
G[to].push_back(Edge(from, 0, G[from].size()-1));
}
void bfs(int s)
{
mem(level,-1);
queue<int> que;
level[s]=0;
que.push(s);
while(!que.empty())
{
int v=que.front(); que.pop();
for(int i=0;i<G[v].size();++i)
{
Edge &e=G[v][i];
if(e.cap>0&&level[e.to]<0)
{
level[e.to]=level[v]+1;
que.push(e.to);
}
}
}
}
int dfs(int v, int t, int f)
{
if(v==t)
return f;
for(int &i=iter[v];i<G[v].size();++i)
{
Edge &e=G[v][i];
if(e.cap>0&&level[v]<level[e.to])
{
int d=dfs(e.to, t, min(f, e.cap));
if(d>0)
{
e.cap-=d;
G[e.to][e.rev].cap+=d;
return d;
}
}
}
return 0;
}
int max_flow(int s, int t)
{
int flow=0;
for(;;)
{
bfs(s);
if(level[t]<0)
return flow;
mem(iter,0);
int f;
while((f=dfs(s, t, INF))>0)
flow+=f;
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&N,&M);
// 0 s
// 1 ~ N 节点
// N+1 t
int s=0,t=N+1;
for(int i=0;i<=N+1;++i)
G[i].clear();
mem(in,0);
mem(out,0);
for(int i=0;i<M;++i)
{
int u,v,type;
scanf("%d%d%d",&u,&v,&type);
++out[u];
++in[v];
if(type==0)
add_edge(u,v,1);
}
bool flag=true;
for(int i=1;i<=N;++i)
{
int cha=out[i]-in[i];
if(cha&1)//入度和出度永远不会相等
{
flag=false;
break;
}
if(cha>0)
add_edge(s,i,cha/2);
else add_edge(i,t,-cha/2);
}
if(!flag)
{
puts("impossible");
continue;
}
max_flow(s,t);
for(int i=0;i<G[s].size();++i)
if(G[s][i].cap!=0)//检查是否每个节点的入度都等于出度
{
flag=false;
break;
}
puts(flag?"possible":"impossible");
}
return 0;
}