入门经典_Chap06_例题[四]:最后四题
来源:互联网 发布:cygwin 运行linux程序 编辑:程序博客网 时间:2024/06/05 17:20
UVA - 1572 Self-Assembly
思路
有n(n≤40000)种边上带标号的正方形。每条边上的标号要么为一个大写字母后面跟着一个加号或减号,要么为数字00。
当且仅当两条边的字母相同且符号相反时,两条边能拼在一起(00不能和任何边拼在一起,包括另一条标号为00的边)。
假设输入的每种正方形都有无穷多种,而且可以旋转和翻转,你的任务是判断能否组成一个无限大的结构。每条边要么悬空(不和任何边相邻),要么和一个上述可拼接的边相邻。
(书上原话)本题看上去很难下手,但不难发现“可以旋转和翻转”是一个很有意思的条件,值得推敲。“无限大结构”并不一定能铺满整个平面,只需要能连出一条无限长的“通路”即可。借助于旋转和翻转,可以让这条“通路”总是往右和往下延伸,因此永远不会自交。这样一来,只需以某个正方形为起点开始“铺路”,一旦可以拼上一块和起点一样的正方形,无限重复下去就能得到一个无限大的结构。
可惜这样的分析仍然不够,因为正方形的数目n很大。进一步分析发现:实际上不需要正方形本身重复,而只需要边上的标号重复即可。这样问题就转化为:把标号看成点(一共只有A+~Z+,A-~Z-这52种,因为00不能作为拼接点),正方形看作边,得到一个有向图。则当且仅当图中存在有向环时有解。只需要做一次拓扑排序即可。
所以这是一道拓扑排序题,我也是看了书上的思路才敲出来的,不过想想也有道理,只是看不出来是拓扑排序,而且不知道图在哪而已。。。
这次拓扑是用dfs写的。
代码
int n, maps[maxn][maxn], vis[maxn];char s[15];int getN(int x) { return 2*(s[2*x]-'A') + (s[2*x+1] == '+'? 0 : 1); }void Link(int x, int y) { if(s[2*x] == '0' || s[2*y] == '0') return; int tx = getN(x), ty = getN(y)^1; maps[tx][ty] = 1;}bool dfs(int x) { vis[x] = -1; for(int i = 0; i < 52; ++i) { if(maps[x][i]) { if(vis[i] < 0) return 0; else if(!vis[i] && ! dfs(i)) return 0; } } vis[x] = 1; return 1;}bool topSort() { met(vis, 0); for(int i = 0; i < 52; ++i) if(!vis[i]) { if(!dfs(i)) return 0; } return 1;}int main() { #ifdef _LOCAL IN; #endif // _LOCAL while(scanf("%d", &n) == 1) { met(maps, 0); for(int i = 0; i < n; ++i) { scanf("%s", s); for(int i = 0; i < 4; ++i) for(int j = 0; j < 4; ++j) { if(i!=j) Link(i, j); } } if(topSort()) printf("bounded\n"); else printf("unbounded\n"); } return 0;}
UVA - 1599 Ideal Path
思路
对我而言,这道题让我重新认识了如何记录路径,同时让我重新认识了bfs的结构,每次不一定只出队一个,也不一定只入队一个,bfs比我想的要更灵活。
题目意思是, 给一个n个点m条边(2 ≤ n ≤ 100000,1 ≤ m ≤ 200000)的无向图,每条边上都涂有一种颜色。求从结点1到结点n的一条路径,使得经过的边数尽量少,在此前提下,经过边的颜色序列的字典序最小。一对结点间可能有多条边,一条边可能连接两个相同结点。输入保证结点1可以达到结点n。颜色为1 ~
从终点开始“倒着”BFS,得到每个结点i到终点的最短距离d[i],然后直接从起点开始走,但是每次到达一个新结点时要保证d值恰好减少1(如有多个选择则可以随便走),直到到达终点。
然后再从起点开始按照上述规则走,如果有多种走法,选颜色字典序最小的走;如果有多条边的颜色字典序都是最小,则记录所有这些边的终点,走下一步时要考虑从所有这些点出发的边。
代码
#include <algorithm>#include <iostream>#include <sstream>#include <utility>#include <string>#include <vector>#include <queue>#include <map>#include <set>#include <cstring>#include <cstdio>#include <cmath>#define met(a,b) memset(a, b, sizeof(a));#define IN freopen("in.txt", "r", stdin);using namespace std;typedef long long LL;typedef pair<int, int> Pii;typedef set<int> Si;typedef vector<int> Vi;const int maxn = 1e6 + 100;const int INF = 0x7fffffff;int n, m, a, b, c;int dis[maxn], ans[maxn];struct edge { int to, next, we; edge(){} edge(int t, int n, int w):to(t), next(n), we(w){}}e[maxn];int tot;int head[maxn];bool vis[maxn];void init() { tot = 0; met(head, -1); met(vis, 0); met(dis, 0); met(ans, 0);}void add(int from, int to, int we) { e[tot] = edge(to, head[from], we); head[from] = tot++;}void bfs() { queue<int> q; q.push(n); vis[n] = 1; dis[n] = 0; while(!q.empty()) { int t = q.front(); q.pop(); for(int i = head[t]; i != -1; i = e[i].next) { int tmp = e[i].to; if(!vis[tmp]) { q.push(tmp); vis[tmp] = 1; dis[tmp] = dis[t]+1; } } }}void bfs2() { met(vis, 0); queue<int> q; q.push(1); vis[1] = 1; while(!q.empty()) { int M = INF, D; Vi v; while(!q.empty()) { int t = q.front();q.pop(); for(int i = head[t]; i != -1; i = e[i].next) { int tt = e[i].to; D = dis[t]; if(vis[tt] || dis[t] != dis[tt]+1 || e[i].we > M) continue; if(e[i].we < M) M = e[i].we, v.clear(); v.push_back(tt); } } ans[D] = M; for(int i= 0; i < v.size(); ++i) { if(!vis[v[i]]) q.push(v[i]), vis[v[i]] = 1; } }}int main() { #ifdef _LOCAL IN; #endif // _LOCAL while(scanf("%d%d", &n, &m) == 2) { init(); for(int i = 0; i < m; ++i) { scanf("%d%d%d", &a, &b, &c); if(a != b) add(a, b, c), add(b, a, c); } bfs(); bfs2(); cout << dis[1] <<endl << ans[dis[1]]; for(int i = dis[1]-1; i >= 1; --i) cout << " " << ans[i]; cout << endl; } return 0;}
UVA - 506 System Dependencies
思路
模拟安装软件和卸载软件同时修正相关组件的操作。
这一题感觉还算简单,题目意思并不难懂,也好实现。感觉主要是考察递归。
但是要注意一些细节上的问题,记录各个组件的状态,关系(依赖关系)等。
代码
#include <algorithm>#include <iostream>#include <sstream>#include <utility>#include <string>#include <vector>#include <queue>#include <map>#include <set>#include <cstring>#include <cstdio>#include <cmath>#define met(a,b) memset(a, b, sizeof(a));#define IN freopen("in.txt", "r", stdin);using namespace std;typedef long long LL;typedef pair<int, int> Pii;typedef map<string, int> Msi;typedef set<int> Si;typedef vector<int> Vi;const int maxn = 1e6 + 100;const int INF = 0x7fffffff;string s, t1, t2, name[maxn];stringstream ss;Msi m;Vi depd[maxn], dept[maxn], insed;int state[maxn]; //0 1 2 未装 隐式 显式int getID(string str) { if(m.count(str)) return m[str]; name[m.size()+1] = str; return m[str] = m.size();}void depend() { ss.clear(); ss << s; ss >> t1 >> t1; int id = getID(t1); while(ss >> t2) { depd[id].push_back(getID(t2)); dept[getID(t2)].push_back(id); }}void install(int id, int ok) { if(state[id] != 0) { if(!ok) cout << " " << name[id] <<" is already installed."<<endl; return; } for(int i = 0; i < depd[id].size(); ++i) { install(depd[id][i], 1); } state[id] = ok?1:2; cout << " Installing " << name[id] <<endl; insed.push_back(id);}bool need(int x) { for(int i = 0; i < dept[x].size(); ++i) { if(state[dept[x][i]] != 0 ) return 1; } return 0;}void remov(int id, int ok) { if(state[id] == 0) { if(!ok) cout << " " << name[id] << " is not installed." <<endl; } else if((!ok || state[id] == 1) && !need(id)) { state[id] = 0; cout << " Removing " << name[id] <<endl; insed.erase(remove(insed.begin(), insed.end(), id), insed.end()); for(int i = depd[id].size()-1; i >= 0; --i) { remov(depd[id][i], 1); } return; } else if(!ok) cout << " " << name[id] << " is still needed." <<endl;}void List() { for(int i = 0; i < insed.size(); ++i) { cout << " " << name[insed[i]] <<endl; }}void init() { m.clear(); insed.clear(); for(int i = 0; i < maxn; ++i) depd[i].clear(), dept[i].clear(); met(state, 0);}int main() { #ifdef _LOCAL IN; #endif // _LOCAL init(); while(getline(cin, s)) { cout << s <<endl; if(s == "END") { init(); continue; } if(s[0] == 'D') { depend(); } else if(s[0] == 'I') { ss.clear(); ss << s; ss >> t1 >> t1; install(getID(t1), 0); } else if(s[0] == 'R') { ss.clear(); ss << s; ss >> t1 >> t1; remov(getID(t1), 0); } else if(s[0] == 'L') { List(); } } return 0;}
UVA - 11853 Paintball
思路
特别好的一道题。
题目意思是,有一个1000×1000的正方形战场,战场西南角的坐标为(0,0),西北角的坐标为(0,1000)。
战场上有n(0≤n≤1000)个敌人,第i个敌人的坐标为(x i ,y i ),攻击范围为r i 。
为了避开敌人的攻击,在任意时刻,你与每个敌人的距离都必须严格大于它的攻击范围。
你的任务是从战场的西边(x=0的某个点)进入,东边(x=1000的某个点)离开。
如果有多个位置可以进/出,你应当求出最靠北的位置。
输入每个敌人的x i 、y i 、r i ,输出进入战场和离开战场的坐标。
这一题个人感觉比较难想的有两点。
第一点是如何判断能不能从西边走到东边,这个应该是此题的核心问题,然后有一个思路是从与上边界相交(切)的每一个圆(每个攻击范围是一个圆)开始,做一遍dfs或bfs,如果在这个过程中有与下边界相交(切)的圆,说明东西被分成了两块,根本无法通过,这个时候结果就是impossible了
但是呢,如果很不幸,在dfs或bfs的过程中,没有圆与下边界相交(切),那么说明你是可以从最西边走到最东边的,那么最北的入口和出口怎么找呢?这是第二个难想的点。
这个时候,我们不妨将dfs(bfs)的过程细分一下,大致有三种情况。
第一种是在这一遍dfs中没有圆与左边界相交,也没有圆与右边界相交,那这个时候最北边应该是1000或者是第一个开始dfs时的那个圆的最南边,如果它与左边界或右边界相交的话;
第二种是在这一遍dfs中有的圆碰到了左边界(右边界同样),那么这个时候自上到左是不是就组成了一个封闭的空间,而你要想出去就只能从下面走,所以这个时候的最北入口应该就是在dfs过程中所有与左边界相交的边的最南边。第三种情况是碰到了右边界,与左边界一样。
然后我们会发现,第一种情况好像结果也在第二,三种情况之内,所有最后的结论就是,我在dfs的过程中,当有的圆与左或右边界相交时,我就取它们最南边的值的最小值,便是入口或者出口了
代码
#include <algorithm>#include <iostream>#include <sstream>#include <utility>#include <string>#include <vector>#include <queue>#include <map>#include <set>#include <cstring>#include <cstdio>#include <cmath>#define met(a,b) memset(a, b, sizeof(a));#define IN freopen("in.txt", "r", stdin);using namespace std;typedef long long LL;typedef pair<int, int> Pii;typedef map<string, int> Msi;typedef set<int> Si;typedef vector<int> Vi;const int maxn = 1e6 + 100;const int INF = 0x7fffffff;const double eps = 1e-2;struct node { int x, y, r;}e[maxn];int n;bool vis[maxn];double et, lv;void init() { met(vis, 0); et = lv = 1000.0; }bool touch(int x, int y) { return (e[x].x-e[y].x)*(e[x].x-e[y].x) + (e[x].y-e[y].y)*(e[x].y-e[y].y) - (e[x].r+e[y].r)*(e[x].r+e[y].r) <= 0;}bool bottom(int id) { return e[id].y-e[id].r <= 0; }bool left(int id) { return e[id].x - e[id].r <= 0; }bool right(int id) { return e[id].x + e[id].r >= 1000; }bool bfs(int id) { queue<int> q; q.push(id); while(!q.empty()) { int t = q.front(); q.pop(); if(bottom(t)) return 1; if(left(t)) et = min(et, e[t].y - sqrt(e[t].r*e[t].r-e[t].x*e[t].x)); if(right(t)) lv = min(lv, e[t].y - sqrt(e[t].r*e[t].r-(1000-e[t].x)*(1000-e[t].x))); for(int i = 0; i < n; ++i) { if(i == t) continue; if(vis[i]) continue; if(touch(t, i)) { vis[i] = 1; q.push(i); } } } return 0;}int main() { #ifdef _LOCAL IN; #endif // _LOCAL while(scanf("%d", &n) == 1) { init(); for(int i = 0; i < n; ++i) { scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].r); } bool ok = 0; for(int i = 0; i < n; ++i) { if(e[i].y+e[i].r >= 1000 && !vis[i]) { vis[i] = 1; if(bfs(i)) { ok = 1; break; } } } if(ok) printf("IMPOSSIBLE\n"); else printf("0.00 %.2lf 1000.00 %.2lf\n", et, lv); } return 0;}
以上。
- 入门经典_Chap06_例题[四]:最后四题
- 入门经典_Chap06_例题[一]:队列,栈,链表的数组实现
- 入门经典_Chap06_例题[二]:二叉树的指针及数组实现
- 入门经典_Chap06_例题[三]:图的搜索 Floodfill 拓扑排序 欧拉回路
- 入门经典_Chap06_习题:搜索 数据结构 欧拉回路
- 文件例题(四)
- javascript四道例题
- 算法竞赛入门经典读书笔记(四)7.3子集生成
- 算法竞赛入门经典读书笔记(四)7.3子集生成
- HTML,CSS和JAVASCRIPT入门经典 笔记(四)
- C#入门经典第6版学习 四
- 四道道经典的笔试题
- 四道Java经典基础题
- Java经典算法题(四)
- Bug经典回放(四)
- Bug经典回放(四)
- Bug经典回放(四)
- Bug经典回放(四)
- Windows 7 登录共享提示“登录失败 禁用当前的账户”解决方案
- Http和Https区别详解
- 怎样正确运用网络舆情大数据?
- Get与Post区别与范例讲解
- 分析linux011版本中inode节点使用直接块,间接块所能表示的一个文件的大小
- 入门经典_Chap06_例题[四]:最后四题
- chrome 打开视频网站提示需要安装flash player(没有安装flash ,请先下载)
- Windows Server R2 2012安装mysql-5.7.13-winx64
- MySQL将查询结果插入到数据表中
- 归并排序求逆序对数 (附另两种姿势BIT 线段树)
- servlet和Jsp生命周期解读
- 【PAT甲级】1078. Hashing (25)
- java-Exectors提供的四种基本线程池用法与比较详解
- Java 图片地址全部路径与相对路径替换