题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3812
题目大意:给定n个字符对ai,bi,表示ai和bi相连,现在要求求出从“sea”连接到"sky"的一个序列,序列中的字符串出现两次,长度要求最大,并且字典序最小,如果没办达到要求,输出"what a pity“。
解题思路:这题是去年武汉邀请赛的题目,难度中上,在做模拟比赛的时候碰到了这题,当时没敢开,难题猛于虎。赛后为了防止比赛时再被虐花点时间过了这题。刚开始想的时候觉得是图论没思路,但一看到最多的字符串种类为16,有戏。就想着状态dp,状态就三万多个,怎么搞都不超时,又看了一下题目,要求输出字典序最小,顿时泄气,还是要搜索啊,那索性直接搜索再加个强力剪枝什么的过了他。
开始的时候把这些字符串用map转化为节点并建图,建好图写了个十分暴力的深搜,试探性地交了下果然Tle。然后开始想如何剪枝,很常规的有两个:1、如果sea和sky不出现在输入中,那他们怎么连接都连接不到对方,人鬼殊途啊。2、如果sea和sky不在一个连通分支,那他们怎么连接都连接不到对方,不是同一个世界的人啊。这两个显然不能有效地增加效率,加上去再交还是Tle。然后开始找性质,其实剪枝就是要求我们找出题目中的性质,然后把不需要搜索的地方排除掉。本题要求的序列中字符串不能出现两次,那我们从sea节点开始遍历,如果某个节点不经过sky就能遍历,那说明在最后的深搜中可以到达,如果某些节点必须要通过sky节点,那说明这个节点必然没办法到达(可以到达的话sky节点就要经过两次),这些符合条件的节点组成几何Set1?同理,可以从end也找一次也能找到一个集合Set2。这样就得到了两个集合Set1和Set2,他们的交集就是可能出现在最后的序列中的节点。记录这个集合大小result,表示所有序列的最大长度,如果第一次碰到序列长度等于result,他就是最后的解(前提是字典序最小,这个在建图前对每个字符串排个序就可以保证)。
举个例子: a <-->sea b<---> sea sky<--> b sky<-->d 那Set1集合为sea a b sky,Set2集合为sky b d sea,他们的交集是sea sky b,只有这三个字符串可能出现在最后的序列中。
测试数据:
10
8
sea a
sea b
sea c
c d
a sky
sky c
sky b
d sky
7
sea pure
pure air
air white
sky white
pure holy
holy white
sky holy
2
sea love
sky love
3
sea blue
blue green
fuckfuck white
代码:
#include <stdio.h>
#include <string.h>
#include <string>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
#define MIN 30
#define MAX 300
#define max(a,b) (a)>(b)?(a):(b)
struct node {
char s1[MAX],s2[MAX];
}ver[MAX];
vector<int> maze[MIN];
map<string,int> mmap;
int n,result,flag,maybe[MIN][4];
int beg,end,maxx,tot,fa[MIN],vis[MIN];
char str[MAX][MIN],ans[MAX][MIN];
int cmp(node a,node b) {
//排序用到的比较函数
if (strcmp(a.s1,b.s1) == 0)
return strcmp(a.s2,b.s2) < 0;
else return strcmp(a.s1,b.s1) < 0;
}
int GetFa(int x) {
//获取父亲节点
int y = fa[x];
while (y != fa[y]) y = fa[y];
return y;
}
void UnionSet(int u,int v) {
//并查集的并操作
int fau = GetFa(u);
int fav = GetFa(v);
if (fav != fau) fa[fau] =fav;
}
void Create_Graph() {
//建好一张有序的图
int i,u,v;
for (i = 1; i <= 2 * n; ++i) {
string tps1 = string(ver[i].s1);
string tps2 = string(ver[i].s2);
if (mmap.find(tps1) == mmap.end())
u = ++tot,mmap[tps1] = u;
else u = mmap[tps1];
if (mmap.find(tps2) == mmap.end())
v = ++tot,mmap[tps2] = v;
else v = mmap[tps2];
maze[u].push_back(v);
UnionSet(u,v);
strcpy(str[u],ver[i].s1);
strcpy(str[v],ver[i].s2);
if (tps1 == "sea") beg = u;
else if (tps2 == "sea") beg = v;
if (tps1 == "sky") end = u;
else if (tps2 == "sky") end = v;
}
}
void LookConn(int k,int flag,int in) {
//寻找从beg开始到不经过end到达的点或者从end开始不经过beg能到达的点
vis[k] = 1,maybe[k][in] = 1;
if (k == flag) return;
for (int i = 0; i < maze[k].size(); ++i) {
int v = maze[k][i];
if (vis[v]) continue;
LookConn(v,flag,in);
}
}
void Dfs(int k,int cnt,int *vis,int *arr) {
//普通深搜,加个剪枝而已
vis[k] = 1,arr[cnt] = k;
if (k == end) {
if (cnt > maxx) {
maxx = cnt;
for (int i = 1; i <= maxx; ++i)
strcpy(ans[i],str[arr[i]]);
}
if (maxx == result) flag = 1;//剪枝,result是能够到达的最多数量,因为有序,此解必是最优
return;
}
for (int i = 0; i < maze[k].size(); ++i) {
int v = maze[k][i];
if (vis[v] == 0 && maybe[v][2] == 1) {
vis[v] = 1;
Dfs(v,cnt+1,vis,arr);
if (flag) return;
vis[v] = 0;
}
}
}
int Solve_1A(){
int i,j,k,arr[MIN];
//寻找可能到达的点
memset(maybe,0,sizeof(maybe));
memset(vis,0,sizeof(vis));
LookConn(beg,end,0);
memset(vis,0,sizeof(vis));
LookConn(end,beg,1);
result = 0;
for (i = 1; i <= tot; ++i) {
if (maybe[i][1] && maybe[i][0])
maybe[i][2] = 1;
if (maybe[i][2]) result++;
}
//赋初值,并从beg开始深搜
memset(vis,0,sizeof(vis));
Dfs(beg,1,vis,arr);
return maxx;
}
int main()
{
int u,v,tpu,tpk;
int i,j,k,t,cas = 0;
scanf("%d",&t);
while (t--) {
scanf("%d",&n);
mmap.clear();
flag = maxx = 0;
tot = beg = end = 0;
for (i = 1; i < MIN; ++i)
maze[i].clear(),fa[i] = i;
for (i = 1; i <= n; ++i) {
//无向处理成有向
scanf("%s%s",ver[i].s1,ver[i].s2);
strcpy(ver[i+n].s1,ver[i].s2);
strcpy(ver[i+n].s2,ver[i].s1);
}
//对节点根据字典序排序,把同一个节点出来的归类
sort(ver+1,ver+1+2*n,cmp);
Create_Graph();
//输出解
printf("Case %d: ",++cas);
if (end == 0 || beg == 0
|| GetFa(beg) != GetFa(end))
printf("what a pity\n");
else {
int maxx = Solve_1A();
for (i = 1; i <= maxx; ++i)
printf(i == maxx ? "%s\n" : "%s ",ans[i]);
}
}
}
本文ZeroClock原创,但可以转载,因为我们是兄弟。