题目:
http://acm.hdu.edu.cn/showproblem.php?pid=5348
题意:
给一个无向图,在各边添加方向使其成为一个有向图,且满足每个点出入度之差的绝对值小于2,若无法满足输出-1
思路:
对于一个无向图,始终存在解;若图不连通,各连通子图之间不互相影响,视为连通图处理即可;对于一个环,其各个点出入度之差必为0,,对于一条线段,除了其起点出度为1,终点入度为1,其他个点出入度之差亦为0,,则可对图做处理,删除每个环,因为其上个点出入度为0不影响,删除环后图成为一个森林,对于森林可以通过分配其子结点线段方向来维护父结点的出入度之差。
在删除环的时候,必须把边一起删去,不然会TLE,之前我是给每条边加了一个标记,表示是否已遍历过,一直TLE,因为这么做在一个点上的边过多时会有极高的时间消耗 = = 加上删边操作first[x] = gnext[i]后AC
代码:
#include <iostream>
#include <stdio.h>
#include <string.h>
#pragma comment (linker, "/STACK:102400000,102400000")
using namespace std;
const int MAXSIZE = 1e5+10;
int ad[MAXSIZE]; //每个点的实际出入度之和
int d[MAXSIZE]; //每个点的当前出入度之和
int io[MAXSIZE];//每个点当前出度-入度的差
bool ans[6 * MAXSIZE];
struct Edge {
int from , to;
bool vis;
};
Edge edges[6 * MAXSIZE];
int first[MAXSIZE], gnext[6 * MAXSIZE], esum;
int n,m;
void addEdge(int from, int to){
gnext[++esum] = first[from];
first[from] = esum;
Edge e = {from, to, false};
edges[esum] = e;
}
void init(){
esum = -1;
memset(first,-1,sizeof(int)*(n+5));
memset(ad,0,sizeof(int)*(n+5));
memset(io,0,sizeof(int)*(n+5));
memset(d,0,sizeof(int)*(n+5));
}
void dfs(int x,bool dir){
int i = first[x];
while (i!=-1){
Edge& e = edges[i];
if (e.vis) {
first[x] = gnext[i];
i=gnext[i];
continue;
}
e.vis = edges[i^1].vis = true;
d[e.from]++;
d[e.to]++;
if (dir){
io[e.from]++;
io[e.to]--;
}
else{
io[e.from]--;
io[e.to]++;
}
//cout<<e.from<<" "<<e.to<<" "<<(i%2)<<" "<<dir<<endl;
ans[i/2] = (i%2)^dir;
first[x] = gnext[i];
dfs(e.to,dir);
return;
}
}
int main(){
int total;
int a,b;
cin>>total;
while (total--){
scanf("%d %d",&n,&m);
init();
for (int i=0;i<m;++i){
scanf("%d %d",&a,&b);
addEdge(a,b);
addEdge(b,a);
ad[a]++;
ad[b]++;
}
//cout<<"done"<<endl;
for (int i=1;i<=n;++i){
while (d[i]!=ad[i]){
//memset(vis,0,sizeof(bool)*(n+5));
if (io[i]<=0) dfs(i,true);
else dfs(i,false);
}
}
for (int i=0;i<m;++i){
printf("%d\n",ans[i]);
}
}
return 0;
}