混合欧拉图
第一次这么长的代码!
hdu 3472 题意:
给出若干字符串,要用字符串来接龙,标号为1的字符串可以反向接,标号为0的只能正向接。
相当于26个字母为结点,每一个字符串是一条从首字母到末尾字母的边,0为有向边,1为无向边。
所以这题是判断混合图中是否有欧拉路问题
本来对于无向边的处理可以拆成两个有向边来做,但由于每一个字符串只能出现一次,如果这样建图可能会有边被流经两次,所以这里不行。
所有的有向边是无法改变的,只能通过预置的方向流动,所以理论上可以对所有的无向边初始任意定一方向,然后判断此时是否存在路,没有的情况下(此时应该会多出两个奇数度的结点,因为总度数是不变的),将连接两个该节点的有向边(原为无向边)反向,再次判断。这里其实是将一个奇数度的结点的一部分入度通过边反向增加到了另一个结点的出度上,实际上相当于 “度数” 这一物品在图中流动运输。
所以使用网络流
在实际程序中,可以先对每一个无向边进行任意的定向,然后将该边插入图中,流量为一。
先用并查集预处理判断是否所有出现结点都联通。
然后计算每个结点的度数,(入度++ , 出度--)如果为0,说明入度 == 出度,如果不为0 ,判断奇偶,只有两个或0个奇数的情况下存在,然后看能否通过改变无向边的方向使所有偶数度点的度数为0,奇数度的点的度数变为1和-1。
这里需要移动的度数和是所有负点(或正点)的度数的一半,求和为sum,然后给所有度数为负的点添加源点到其的边,流量为deg[I]/2,度数为正的点添加到汇点的边,流量为deg[I]/2。最后判断能否满流,能及说明能将所有需要转移的度数转移,即包含欧拉路。
~Can you hear me
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cmath>
#include <set>
#include <map>
#define mod 1000000007
#define low_bit(x) x&(-x)
#define type int
#define maxn 2000
#define maxm 4005
#define INF 1e9
using namespace std ;
typedef long long ll ;
int n;
int s, t;
struct Edge {
int from, to, next;
type cap, flow;
void get (int u, int a, int b, type c, type d) {
from = u; to = a; next = b; cap = c; flow = d;
}
}edge[maxm];
int tol;
int head[maxn];
int gap[maxn], dep[maxn], pre[maxn], cur[maxn];
void init () {
tol = 0;
memset (head, -1, sizeof head);
}
void add_edge (int u, int v, type w, type rw=0) {
//cout << u << " " << v << " " << w << endl;
edge[tol].get(u, v,head[u],w,0);head[u]=tol++;
edge[tol].get(v, u,head[v],rw,0);head[v]=tol++;
}
type sap (int start, int end, int N) {
memset (gap, 0, sizeof gap);
memset (dep, 0, sizeof dep);
memcpy (cur, head, sizeof head);
int u = start;
pre[u] = -1;
gap[0] = N;
type ans = 0;
while (dep[start] < N) {
if (u == end) {
type Min = INF;
for (int i = pre[u]; i != -1; i = pre[edge[i^1].to])
if (Min > edge[i].cap - edge[i].flow)
Min = edge[i].cap-edge[i].flow;
for (int i = pre[u]; i != -1; i = pre[edge[i^1].to]) {
edge[i].flow += Min;
edge[i^1].flow -= Min;
}
u = start;
ans += Min;
continue;
}
bool flag = false;
int v;
for (int i = cur[u]; i != -1; i = edge[i].next) {
v = edge[i].to;
if (edge[i].cap - edge[i].flow && dep[v]+1 == dep[u]) {
flag = true;
cur[u] = pre[v] = i;
break;
}
}
if (flag) {
u = v;
continue;
}
int Min = N;
for (int i = head[u]; i != -1; i = edge[i].next)
if (edge[i].cap - edge[i].flow && dep[edge[i].to] < Min) {
Min = dep[edge[i].to];
cur[u] = i;
}
gap[dep[u]]--;
if (!gap[dep[u]]) return ans;
dep[u] = Min+1;
gap[dep[u]]++;
if (u != start) u = edge[pre[u]^1].to;
}
return ans;
}
int par[30] , rank1[30];
void bing_cha (){
for( int i = 0 ; i < 26 ; i ++ ){
par[i] = i ;
rank1[i] = 0 ;
}
}
int findSet( int x ){
if( x != par[x])
par[x] = findSet(par[x]) ;
return par[x] ;
}
void Union(int x , int y )
{
x = findSet(x) , y = findSet(y) ;
if( x == y ) return ;
if( rank1[x] > rank1[y] )
par[y] = x ;
else{
par[x] = y ;
if( rank1[x] == rank1[y] ) rank1[y] ++ ;
}
}
struct node {
int st , ed , ni , ti ;
node(int a , int b , int c , int d ) : st(a) , ed(b) , ni(c) , ti(d) {}
};
char read[40] ; bool visvis[30] ; int deg[30] ;
int main()
{
int a , b , c, d , time , cas ;
scanf("%d",&cas) ;
for(int tt = 1 ; tt <= cas ; tt ++ ){
bing_cha() ;
scanf("%d",&time ) ;
init();
memset(visvis , 0 , sizeof( visvis )); memset(deg , 0 , sizeof( deg )) ;
s = 26 , t = 27 ; n = t ;int len ;
for(int i = 0 ; i < time ; i ++ ){
scanf("%s %d",read ,&c) ;
len = strlen( read ) ;
a = read[0] - 'a' ;
b = read[len-1] - 'a' ;
visvis[a] = true , visvis[b] = true ;
deg[a] -- , deg[b] ++ ;
Union( a , b ) ;
if( c == 1 ) add_edge(a , b , 1 ) ;
}
bool flag = true ;
for( int i = 0 ; i < 26 ; i ++ ){
if(!visvis[i]) continue ;
for( int j = i+1 ; j < 26 ; j ++ ){
if(!visvis[j]) continue ;
if( findSet(i) != findSet(j)) { flag = false ; break ; }
}
break ;
}
if( !flag ) {printf("Case %d: Poor boy!\n",tt) ; continue ; } //union to eliminate
int v1 = - 1, v2 = -1 , cnt = 0 ;
for( int i = 0 ; i < 26 ; i ++ ){
if(deg[i] % 2 == 1 || deg[i] % 2 == -1){
cnt ++ ;
if(deg[i] > 0 ) v2 = i ;
if( deg[i] < 0 ) v1 = i ;
}
}
if( !cnt || (cnt == 2 && v1 != -1 && v2 != -1 ) ) {
if( cnt == 2 ) add_edge( v2 , v1 , 1 ) ;
}
else { flag = false ; printf("Case %d: Poor boy!\n",tt) ; continue ; }
int sum = 0 ;
for( int i = 0 ; i < 26 ; i ++ ){
if( deg[i] < 0 )
{
add_edge( s , i , - deg[i] / 2 ) ;
sum += - deg[i] / 2 ;
}
else
add_edge( i , t , deg[i] / 2 ) ;
}
int ans = sap(s , t , n) ;
if( flag && ans == sum)
printf("Case %d: Well done!\n",tt) ;
else
printf("Case %d: Poor boy!\n",tt) ;
}
return 0;
}