UVa 10129 Play on Words


此页面通过工具从 csdn 导出,格式可能有问题。


好久不写代码果然手会生成屎

题目在 poj上也有


题意就是给你一堆单词,按首位顺序排列起来,问你有没有解。

第一眼看上去是个哈密顿通路,单词当做节点,首尾关系作边,听上去妥妥的不过N有100000 复杂度太恐怖。

其实是从刘汝佳的小白书第二版(其实该叫小紫红书了)上欧拉回路那节看到的,所以得考虑考虑转换

把单词当边,首尾字符当做点 题目就转变为 欧拉通路


不得不感慨,图论算法的重点还是在构图!!!


判断欧拉通路:

1. 一个有向图存在欧拉回路必定也存在欧拉通路...因为通路的定义包括了回路
2. 不考虑为欧拉回路的情况..一个有向图是欧拉通路就必须有一个点出度-入度=1,一个点入度-出度=1..这两点就是欧拉通路的起点和终点..并且该图连通

3. 我自己加一条,其实是对2的理解: 入读-出度>1 时直接return false ,排除 a->b a->c 这种情况


判断欧拉回路:

无向图是欧拉图的充要条件是所有点的度为偶数并且所有点联通
有向图是欧拉图的充要条件是所有点的入度=出度..并且联通...


判断连通

1. dfs遍历, 这里有一份参考代码 不过我一直怀疑其正确性。因为你无法确定有向图的起点,比如 a->b->c 如果你从b点开始搜的话就会悲催,但是你不知道b点上面还有没有点。

2. 并查集,并查集的方便之处不用废话。但是在图论中两点之间有多条边时需要注意一些细节的处理。


关于判断入读出度

一种办法是分别记录 in[] out[]

我用的办法是一个数组:sz[x]++ 表示拉出一条边,sz[x]--表示走来一条边,sz[x]==1 sz[x]==-1 就是上面的两种情况


上代码吧

这是只开sz[]的

#include <iostream>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#define N 101010
using namespace std;

int sz[333]; int fa[333]; bool ok[333]; // 数据中使用到的单词 int n, T;

int getfa(int v) { if ( fa[v] == v ) return v; return fa[v] = getfa(fa[v]); }

void init() { memset(ok, 0, sizeof(ok)); memset(sz, 0, sizeof(sz)); cin >> n; string a; for ( int i(‘a’); i <= ‘z’; i++) fa[i] = i; for ( int i(1); i <= n; i++) { cin >> a; int x = a[0], y = a[a.size() - 1]; fa[getfa(x)] = getfa(y); ok[x] = ok[y] = true; sz[x]++; sz[y]–; } }

bool check() { int tf = 0 , i; for ( i = ‘a’; i <= ‘z’; i++) { if (ok[i]) { if ( !tf ) tf = getfa(i); else if ( getfa(i) != tf ) return false; // 不连通 } }

int c1 = 0, c2 = 0;
for ( int i('a'); i &lt;= 'z'; i++)
{
    if (!ok[i]) continue;
    if ( sz[i] == 0 ) continue;
    else if ( sz[i] == 1 ) c1++;
    else if ( sz[i] == -1) c2++;
    else return false;				//  其它乱七八糟的情况 比如 ab ab out[a]=2 in[b]=2 
}

if ( (c1 == 1 &amp;&amp; c2 == 1) || (c1 == 0 &amp;&amp; c2 == 0) ) //  单向路径 or 回路
    return true;
else
    return false;

}

int main() { freopen(“in.txt”, “r”, stdin); for ( cin >> T; T–; ) { init(); if (check()) cout << “Ordering is possible.” << endl; else cout << “The door cannot be opened.” << endl; } }



为保险起见,还是开了两个数组的

#include <iostream>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#define N 101010
using namespace std;

int in[333], out[333]; int fa[333]; bool ok[333]; // 数据中使用到的单词 int n, T;

int getfa(int v) { if ( fa[v] == v ) return v; return fa[v] = getfa(fa[v]); }

void init() { memset(ok, 0, sizeof(ok)); memset(in, 0, sizeof(in)); memset(out, 0, sizeof(out)); cin >> n; string a; for ( int i(‘a’); i <= ‘z’; i++) fa[i] = i; for ( int i(1); i <= n; i++) { cin >> a; int x = a[0], y = a[a.size() - 1]; fa[getfa(x)] = getfa(y); ok[x] = ok[y] = true; out[x]++; in[y]++; } }

bool check() { int tf = 0 , i; for ( i = ‘a’; i <= ‘z’; i++) { if (ok[i]) { if ( !tf ) tf = getfa(i); else if ( getfa(i) != tf ) return false; // 不连通 } }

int c1 = 0, c2 = 0;
for ( int i('a'); i &lt;= 'z'; i++)
{
    if (!ok[i]) continue;
    if ( in[i] == out[i] ) continue;
    else if ( in[i] - out[i] == 1 ) c1++;
    else if ( in[i] - out[i] == -1) c2++;
    else return false;
    //else {printf("%d%d\n",in[i],out[i]);return false;}          //  其它乱七八糟的情况
}

if ( (c1 == 1 &amp;&amp; c2 == 1) || (c1 == 0 &amp;&amp; c2 == 0) ) //  单向路径 or 回路
    return true;
else
    return false;

}

int main() { //freopen(“in.txt”, “r”, stdin); for ( cin >> T; T–; ) { init(); if (check()) cout << “Ordering is possible.” << endl; else cout << “The door cannot be opened.” << endl; } }






Avatar
huiren
Code Artisan

问渠那得清如许,为有源头活水来

相关

下一页
上一页
comments powered by Disqus