题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5348
题目大意:给出一幅无向图,问是否存在一种方案,使得给每条边赋予方向后,每个点的入度与出度之差小于等于1。如不存在,输出-1;存在,则按边的输入顺序输出方向。
比如:若给出第 m 条边为 u,v,则在对应第m行输出0表示u->v,输出1表示u<-v。
解题思路:
首先证明一定可以构造出符合要求的图。
对于一个环,显然可以使得其内的每个点入度与出度相等。之后可以把环看成点。
对于一棵树,则可以通过均分其子结点使得入度与出度之差小于等于1。
因此可以看作是把图分成由环和点构成的森林。则一定有解。
dfs遍历,首先遍历度数为奇数的点,因为这些点一定是起点或者终点。遍历完之后删去遍历过的边。
再遍历剩下的边。
代码中给ans[(id>>1)+1]的赋值,是因为每输入一条边,都向邻接表中加入两条有向边,因此边的本身序号 id 与在邻接表中的序号 id‘ 的关系为
id‘>>1=id。这里 id‘ 为(0,1),(2,3)....因此直接除以2得到对应的序号。+1则只是为了使其在ans数组中从 1 开始储存。看个人习惯。
也算是被逼着学了下数组的邻接表用法。之前只会用vector的邻接表。
对结点 i ,head[i]表示以结点 i 为起点的最后一条加入的边,初始化为-1。
对边 j ,pre[j]表示与边 j 同起点的上一条边的编号,或者说再Edge数组中的序号;nxt[j]则表示同起点的下一条边的编号。
可以用to[j]表示边 j 的终点。也可以用结构体维护起点和终点的关系。
cost[j] 表示边 j 的边权。
代码如下:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define N 200005 #define M 600005 struct edge{ int u,v; edge(){} edge(int u,int v):u(u),v(v){} }Edge[M]; int head[N],pre[M],nxt[M],du[N],ans[M>>1],E; int n,m,t,a,b; void init(){ memset(head,-1,sizeof(head)); memset(nxt,-1,sizeof(nxt)); memset(du,0,sizeof(du)); E=0; } void addedge(int u,int v){ Edge[E]=edge(u,v); pre[E]=head[u]; head[u]=E; E++; Edge[E]=edge(v,u); pre[E]=head[v]; head[v]=E; E++; } void dfs(int u){ while(head[u]!=-1){ du[u]--; int id=head[u],v=Edge[id].v; if(id&1) ans[(id>>1)+1]=0; else ans[(id>>1)+1]=1; int Pre,Nxt; head[u]=pre[id]; Pre=pre[id]; Nxt=nxt[id]; if(Pre!=-1) nxt[Pre]=Nxt; if(Nxt!=-1) pre[Nxt]=Pre; id^=1; if(head[v]==id) head[v]=pre[id]; Pre=pre[id]; Nxt=nxt[id]; if(Pre!=-1) nxt[Pre]=Nxt; if(Nxt!=-1) pre[Nxt]=Pre; u=v; if(du[v]) du[v]--; } } int main(){ scanf("%d",&t); while(t--){ init(); scanf("%d%d",&n,&m); for(int i=0;i<m;i++){ scanf("%d%d",&a,&b); addedge(a,b); du[a]++,du[b]++; } for(int i=0;i<E;i++) if(pre[i]!=-1) nxt[pre[i]]=i; for(int i=1;i<=n;i++) nxt[head[i]]=-1; for(int i=1;i<=n;i++) if(du[i]&1) dfs(i); for(int i=1;i<=n;i++) if(du[i]) dfs(i); for(int i=1;i<=m;i++) printf("%d\n",ans[i]); } return 0; }
时间: 2024-10-16 16:48:12