Hdu 3812 Sea Sky (模拟_搜索)
来源:互联网 发布:define js 模块 编辑:程序博客网 时间:2024/06/07 05:15
原文链接:http://blog.csdn.net/woshi250hua/article/details/7614161
题目链接: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原创,但可以转载,因为我们是兄弟。
- Hdu 3812 Sea Sky (模拟_搜索)
- hdu 3812 Sea Sky 深搜+剪枝
- HDU 3683 模拟&搜索
- HDU 3683--模拟+搜索
- hdu 4431 Mahjong (模拟+搜索)
- HDU 4431 Mahjong 搜索 模拟
- hdu 2097 Sky数
- HDU 2097 Sky数
- hdu Sky数
- HDU 2097 Sky数.
- HDU 2097 Sky数
- Sky数(HDU 2097)
- HDU 2097 Sky数
- HDU 2097 Sky数
- hdu-2097-Sky数
- hdu-2097-Sky数
- HDU 2097 Sky 数
- hdu-2097-Sky数
- 《老人与海》后感
- “荒谬”的等式:1+2+3+4+…+∞= -1/12;1^2+2^2+3^2+4^2+…+∞^2=0
- HTML5中应该记住的知识
- <第一行代码>笔记一
- Java的方法重写规则
- Hdu 3812 Sea Sky (模拟_搜索)
- 欢迎使用CSDN-markdown编辑器
- WebSite for Developers
- 完全打开lk log的方法
- java引用传递值传递的"深入"解析与c++中的值传递
- freemarker
- 两种方法实现用CSS切割图片只取图片中一部分
- linux文件比较命令
- C++实现最长公共子序列和最长公共子串