您的位置:首页 > 理论基础 > 计算机网络

HDU 3472 HS BDC 混合图的欧拉路径判断 (网络流)

2017-10-11 21:54 387 查看

HDU 3472 HS BDC 混合图的欧拉路径判断

题意:给n个字符串,这些字符串如果某个字符串的尾部字母和另一个字符串的首部字母相等,那么这两个字母就可以连起来,有的字符串可以反转,问是否存在一种情况使得所有字符串都连成一条链。

思路:

字符串的首字母–>字符串的尾字母建一条有向边,如果字符串是可反转的,那么就建无向边,

那么问题就转换为:

是否存在一种无向边定向方案,使得这个图中存在这么一条欧拉路径。

给欧拉路径的终点->起点(度数为奇的点)加条边,边权为1,使之成为欧拉回路。

首先任意定向无向边为有向边。建图的时候,若为无向边则加边,方向任意定,边权为1。否则不加边,因为有向边已经不能改变,然后记录每个节点的出度和入度的差。

设差为k,k如果为偶数的话,那么如果我改变k/2条与这个节点相关的边就可以使得这个节点的 出度=入度。

k如果为奇数的话,假设有两个节点的k为奇数那么其中一个是起点另一个是终点。从终点到起点建一条边,边权为1

如果差为k>0,那么说明 出度>入度,需要减少出度增加入度来使得 出度=入度,超级源点->该点,边权为k/2

如果差为k<0,那么说明 出度<入度,需要增加出度减少入度来使得 出度=入度,该点->超级汇点,边权为-k/2

如果差为k=0,那么说明 出度=入度。

如此建图之后判满流,如果满流则说明存在欧拉回路,是欧拉图。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <set>
#include <algorithm>
#include <limits.h>
#define LL long long
#define N 1010
#define M 10010
#define inf 10000000
using namespace std;

const int INF = INT_MAX;

struct Edge{
int from, to, w, nxt;
}ed[M << 2];

int head
, idc = 1, S, T;
int dis
;
int top, sta
, last
;

void init(){
memset(head, 0, sizeof(head));
idc = 1;
}

void adde(int u, int v, int w){
ed[++idc].from = u;
ed[idc].to = v;
ed[idc].w = w;
ed[idc].nxt = head[u];
head[u] = idc;
}

bool bfs(){
memset(dis, -1, sizeof(dis));
queue <int> q; q.push(S); dis[S]=0;
while( !q.empty() ){
int u = q.front(); q.pop();
for(int k=head[u]; k; k=ed[k].nxt) {
int v = ed[k].to;
if(ed[k].w && dis[v] == -1){
dis[v] = dis[u] + 1;
q.push( v );
if(v == T) break;
}
}
}
memcpy(last, head, sizeof(head));
return dis[T] != -1;
}

int find(int u, int low){
if (u==T || low==0) return low;
int totflow =0;
for(int k=last[u]; k; last[u]=k=ed[k].nxt){
int v = ed[k].to;
if(ed[k].w && dis[v] == dis[u] + 1){
int f = find(v, min(low,ed[k].w));
ed[k].w -= f; ed[k^1].w += f;
totflow += f; low -= f;
if(low == 0) return totflow;
}
}
if( !totflow ) dis[u] = -1;
return totflow;
}

LL Dinic(){
LL ans = 0;
while (bfs()) ans += find(S, INF);
return ans;
}

int du[30];
int fa[30], use[30];

int findfa(int x){
return x==fa[x] ? x : fa[x] = findfa(fa[x]);
}

int Union(int x, int y){
x = findfa(x), y = findfa(y);
if(x == y) return false;
fa[x] = y;
return true;
}

int main(){
int t, n, cas=0;
char ch[30];
scanf("%d", &t);
while( t-- ){
scanf("%d", &n);
init();
for(int i=0; i<26; i++){
fa[i] = i; use[i] = 0; du[i] = 0;
}
for(int i=0; i<n; i++){
int x; scanf("%s%d", ch, &x);
int st = ch[0] - 'a';
int ed = ch[strlen(ch) - 1] - 'a';
du[st]++; du[ed]--;
use[st] = 1; use[ed] = 1;
if( x ) adde(st, ed, 1), adde(ed, st, 0);
Union(st, ed);
}
int num1 = 0, num2 = 0;
S = -1, T = -1;
for(int i=0; i<26; i++)
if( use[i] ){
if(fa[i] == i) ++num1;//连通块个数
if(du[i] % 2){
++num2;//奇度数的点的个数(作为欧拉路径的起始点,终止点)
if(du[i] < 0) S = i;
if(du[i] > 0) T = i;
}
}
printf("Case %d: ", ++cas);
if(num1!=1 || num2==1 || num2>2 || (num2==2 && (S==-1 || T==-1))){
puts("Poor boy!"); continue;
}
if(num2 == 2) adde(T, S, 1), adde(S, T, 0);
S = 26, T = 27;
LL sum = 0;
for(int i=0; i<26; i++)  //流量为1,度数改变2
if(du[i] > 0) adde(S, i, du[i] >> 1), adde(i, S, 0), sum += du[i] >> 1;
else if(du[i] < 0) adde(i, T, -du[i] >> 1), adde(T, i, 0);
if(Dinic() != sum) puts("Poor boy!");
else puts("Well done!");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: