dfs && bfs
来源:互联网 发布:追梦格林体测数据 编辑:程序博客网 时间:2024/05/16 15:21
图的遍历、或者答案的搜索。
两种最简单的搜索、遍历方法。
dfs:
入门:连通块
572 uva
#include<bits/stdc++.h>using namespace std;//#define ONLINE_JUDGE#define eps 1e-8#define inf 0x3f3f3f3f#define INF 0x7fffffff#define INFL 0x3f3f3f3f3f3f3f3fLL#define enter putchar(10)#define rep(i,a,b) for(int i = (a); i < (b); ++i)#define repe(i,a,b) for(int i = (a); i <= (b); ++i)#define mem(a,b) (memset((a),b,sizeof(a)))#define sf(a) scanf("%d",&a)#define sfI(a) scanf("%I64d",&a)#define sfd(a,b) scanf("%d%d",&a,&b)#define sft(a,b,c) scanf("%d%d%d",&a,&b,&c)#define sfs(a) scanf("%s",a)#define pf(a) printf("%d\n",a)#define pfd(a,b) printf("%d %d\n",a,b)#define pfP(a) printf("%d %d\n",a.fi,a.se)#define pfs(a) printf("%s\n",a)#define pfI(a) printf("%I64d\n",a)#define ds(a) int a; sf(a)#define PR(a,b) pair<a,b>#define fi first#define se second#define LL long long#define DB doubleconst double PI = acos(-1.0);const double E = exp(1.0);template<class T> T gcd(T a, T b) {return b ? gcd(b, a % b) : a;}template<class T> T lcm(T a, T b) {return a / gcd(a, b) * b;}template<class T> inline T Min(T a, T b) {return a < b ? a : b;}template<class T> inline T Max(T a, T b) {return a > b ? a : b;}int n, m;#define N 110char pic[N][N];int vis[N][N];int cnt;int dirx[3] = {-1, 1, 0};int diry[3] = {-1, 1, 0};void dfs(int u, int v, int id) { rep(i, 0, 3) { rep(j, 0, 3) { if(!vis[u + dirx[i]][v + diry[j]] && pic[u + dirx[i]][v + diry[j]] == '@') { vis[u + dirx[i]][v + diry[j]] = id; dfs(u + dirx[i], v + diry[j], id); } } }}void check() { repe(i, 1, n) { repe(j, 1, m) { printf("%d%c", vis[i][j], j == m ? '\n' : ' '); } }}int main() {#ifndef ONLINE_JUDGE freopen("in.txt", "r", stdin);// freopen("Out.txt", "w", stdout);#endif while(~sfd(n, m)) { if(!n && !m) break; cnt = 0; mem(vis, 0); repe(i, 1, n) sfs(pic[i] + 1); mem(pic[n + 1], '\0'); repe(i, 1, n) { repe(j, 1, m) { if(!vis[i][j] && pic[i][j] == '@') { dfs(i, j, ++cnt); } } } pf(cnt);// check(); } return 0;}
没啥好说的。。
下面一个难点点的题,也是连通块
1103 uva
识别一些象形文字,给你图片对应,并且任一文字可以拉伸变型但不能拉断,给你一张16进制表示的图片,问你图片里的字幕是些什么,按字典序排好然后输出。
推荐个博客,思路特别清晰
http://www.bubuko.com/infodetail-713914.html
注意到每个文字的“洞”都不同,所以dfs洞的个数就能知道是什么图像。
所以需要三种dfs
1、处理边界
2、处理文字边界
3、处理“洞”
思路清晰就好做:
#include<bits/stdc++.h>using namespace std;//#define ONLINE_JUDGE#define eps 1e-8#define inf 0x3f3f3f3f#define INF 0x7fffffff#define INFL 0x3f3f3f3f3f3f3f3fLL#define enter putchar(10)#define rep(i,a,b) for(int i = (a); i < (b); ++i)#define repe(i,a,b) for(int i = (a); i <= (b); ++i)#define mem(a,b) (memset((a),b,sizeof(a)))#define sf(a) scanf("%d",&a)#define sfI(a) scanf("%I64d",&a)#define sfd(a,b) scanf("%d%d",&a,&b)#define sft(a,b,c) scanf("%d%d%d",&a,&b,&c)#define sfs(a) scanf("%s",a)#define pf(a) printf("%d\n",a)#define pfd(a,b) printf("%d %d\n",a,b)#define pfP(a) printf("%d %d\n",a.fi,a.se)#define pfs(a) printf("%s\n",a)#define pfI(a) printf("%I64d\n",a)#define ds(a) int a; sf(a)#define PR(a,b) pair<a,b>#define fi first#define se second#define LL long long#define DB doubleconst double PI = acos(-1.0);const double E = exp(1.0);template<class T> T gcd(T a, T b) {return b ? gcd(b, a % b) : a;}template<class T> T lcm(T a, T b) {return a / gcd(a, b) * b;}template<class T> inline T Min(T a, T b) {return a < b ? a : b;}template<class T> inline T Max(T a, T b) {return a > b ? a : b;}int n, m;#define N 210char pic[N][N];char od[N];char tab[10] = "WAKJSD\0";char ans[1000];void mark(int r, int c, int p) { //标记 int num = 0; if(isalpha(od[p])) num = od[p] - 'a' + 10; else num = od[p] - '0'; rep(i, 0, 4) { pic[r][c + (3 - i)] = num % 2 + '0'; num >>= 1; }}void Init() { //读入 mem(pic, '\0'); rep(i, 0, n) { sfs(od); rep(j, 0, m) { mark(i, j * 4, j); } }}void check() { //检查 rep(i, 0, n) { pfs(pic[i]); } enter; enter;}void dfs_boundary(int r, int c) { //dfs上下左右的边界 if(r >= n || r < 0 || c >= 4 * m || c < 0) return ; if(pic[r][c] != '0') return ; pic[r][c] = '-'; for(int i = -1; i <= 1; i++) { for(int j = -1; j <= 1; j++) { if(i == j || i == -j) continue; dfs_boundary(r + i, c + j); } }}void operat_boundary() { //处理上下左右的边界 rep(i, 0, n) { if(pic[i][0] == '0') dfs_boundary(i, 0); if(pic[i][m * 4 - 1] == '0') dfs_boundary(i, m * 4 - 1); } rep(i, 0, m * 4) { if(pic[0][i] == '0') dfs_boundary(0, i); if(pic[n - 1][i] == '0') dfs_boundary(n - 1, i); }}int hole;void dfs_image_inside(int r, int c) { //处理文字内部 if(r < 0 || r >= n || c < 0 || c >= 4 * m) return ; if(pic[r][c] != '0') return ; pic[r][c] = '-'; rep(i, -1, 2) { rep(j, -1, 2) { if(i == j || i == -j) continue; dfs_image_inside(r + i, c + j); } }}void dfs_image_boundary(int r, int c) { //处理文字边界 if(r < 0 || r >= n || c < 0 || c >= 4 * m) return ; if(pic[r][c] == '1') { pic[r][c] = '*'; rep(i, -1, 2) { rep(j, -1, 2) { if(i == j || i == -j) continue; dfs_image_boundary(r + i, c + j); } } } if(pic[r][c] == '0') { hole++; dfs_image_inside(r, c); }}void solve() { mem(ans, '\0'); int cnt = 0; int bb = m * 4; rep(i, 0, n) { rep(j, 0, bb) { if(pic[i][j] == '1') { hole = 0; dfs_image_boundary(i, j); ans[cnt++] = tab[hole]; } } } ans[cnt] = '\0'; sort(ans, ans + cnt); pfs(ans);}int main() {#ifndef ONLINE_JUDGE freopen("in.txt", "r", stdin);// freopen("Out.txt", "w", stdout);#endif int cas = 0; while(~sfd(n, m) && (n || m)) { Init(); operat_boundary(); printf("Case %d: ", ++cas); solve();// check(); } return 0;}
总结:
做这种题目除了一开始想的清晰之外,最好可以把图的样子打表打出来,(check()函数的作用就是这个),这样不容易出错,也方便想问题。
dfs 拓扑排序
uva 10305 拓扑排序裸题
有n个变量,m个二元组(u, v),表示u < v。 那么所有变量从小到大排列起来可能是什么样子?
SampleInput
5 4
1 2
2 3
1 3
1 5
0 0
SampleOutput
1 4 2 5 3
看代码:
#include<bits/stdc++.h>using namespace std;//#define ONLINE_JUDGE#define eps 1e-8#define inf 0x3f3f3f3f#define INF 0x7fffffff#define INFL 0x3f3f3f3f3f3f3f3fLL#define enter putchar(10)#define rep(i,a,b) for(int i = (a); i < (b); ++i)#define repe(i,a,b) for(int i = (a); i <= (b); ++i)#define mem(a,b) (memset((a),b,sizeof(a)))#define sf(a) scanf("%d",&a)#define sfI(a) scanf("%I64d",&a)#define sfd(a,b) scanf("%d%d",&a,&b)#define sft(a,b,c) scanf("%d%d%d",&a,&b,&c)#define sfs(a) scanf("%s",a)#define pf(a) printf("%d\n",a)#define pfd(a,b) printf("%d %d\n",a,b)#define pfP(a) printf("%d %d\n",a.fi,a.se)#define pfs(a) printf("%s\n",a)#define pfI(a) printf("%I64d\n",a)#define ds(a) int a; sf(a)#define PR(a,b) pair<a,b>#define fi first#define se second#define LL long long#define DB doubleconst double PI = acos(-1.0);const double E = exp(1.0);template<class T> T gcd(T a, T b) {return b ? gcd(b, a % b) : a;}template<class T> T lcm(T a, T b) {return a / gcd(a, b) * b;}template<class T> inline T Min(T a, T b) {return a < b ? a : b;}template<class T> inline T Max(T a, T b) {return a > b ? a : b;}int n, m;#define N 105int vis[N]; //记录访问状态的数组bool G[N][N]; //有向图int ans[N]; //保存答案的数组int t; //ans数组的下标bool dfs(int u) { vis[u] = -1; //先把vis[u]置-1表示正在访问 repe(v, 1, n) { //依次访问所有大于u的点v if(G[u][v]) { if(vis[v] < 0) return false;//如果v也正在访问,则存在环,返回false if(!vis[v] && !dfs(v)) return false;//如果dfs(v)发现有环,返回false } } vis[u] = 1; //访问完u和所有比u大的点之后把u置1 ans[t--] = u; //把u加入到ans数组里(注意,t是全局变量,加入u时能够保证所有比u大的都已经加入到了ans中) return true;}bool tpsort() { //拓扑排序 t = n; repe(i, 1, n) { //如果点i没被访问过,那么访问i if(!vis[i]) { if(!dfs(i)) return false; } } repe(i, 1, n) printf("%d%c", ans[i], i == n ? '\n' : ' '); //输出答案 return true;}int main() {#ifndef ONLINE_JUDGE freopen("in.txt", "r", stdin);// freopen("Out.txt", "w", stdout);#endif while(~sfd(n, m) && n) { int u, v; mem(G, false); mem(vis, 0); while(m--) { sfd(u, v); G[u][v] = true; } tpsort(); } return 0;}
10562 uva 看图画树
不用建树,直接递归输出就行,注意空树情况。怎么判断是否有子树,怎么判断哪些是子树,都要注意。
/* * * Author : Triose * Email : Triose@163.com * Update_time : 2016.6.12 * *///#include<bits/stdc++.h>#include<stdio.h>#include<iostream>#include<string>#include<string.h>#include<algorithm>#include<vector>#include<queue>#include<stack>#include<iterator>#include<math.h>#include<stdlib.h>#include<time.h>#include<map>#include<set>using namespace std;//#define ONLINE_JUDGE#define eps 1e-8#define inf 0x3f3f3f3f#define INF 0x7fffffff#define INFL 0x3f3f3f3f3f3f3f3fLL#define enter putchar(10)#define rep(i,a,b) for(int i = (a); i < (b); ++i)#define repe(i,a,b) for(int i = (a); i <= (b); ++i)#define mem(a,b) (memset((a),b,sizeof(a)))#define sf(a) scanf("%d",&a)#define sfI(a) scanf("%I64d",&a)#define sfd(a,b) scanf("%d%d",&a,&b)#define sft(a,b,c) scanf("%d%d%d",&a,&b,&c)#define sfs(a) scanf("%s",a)#define pf(a) printf("%d\n",a)#define pfd(a,b) printf("%d %d\n",a,b)#define pfp(a) printf("%d %d\n",a.fi,a.se)#define pfs(a) printf("%s\n",a)#define pfI(a) printf("%I64d\n",a)#define PR(a,b) pair<a,b>#define ds(t) int t; sf(t)#define fi first#define se second#define LL long long#define DB doubleconst double PI = acos(-1.0);const double E = exp(1.0);template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }template<class T> inline T Min(T a, T b) { return a < b ? a : b; }template<class T> inline T Max(T a, T b) { return a > b ? a : b; }int n, m;#define N 210char pic[N][N];void dfs(int u, int v) { putchar(pic[u][v]); putchar('('); if(u + 1 < n && pic[u + 1][v] == '|') { //如果有子树 int lft = v; //左边界 while(lft - 1 >= 0 && pic[u + 2][lft - 1] == '-') lft--; for(; pic[u + 2][lft] == '-' && pic[u + 3][lft] != '\0'; lft++) { if(!isspace(pic[u + 3][lft])) dfs(u + 3, lft); //fgets得到的串,'\n'也满足isspace } } putchar(')');}void solve() { n = 0; while(1) { fgets(pic[n], N, stdin); if(*pic[n] == '#') break; n++; } printf("("); if(n) { //不加的话可以用1 \n #试试,会出错,注意空树的情况 rep(i, 0, N) { if(pic[0][i] != ' ') { dfs(0, i); break; } } } printf(")\n");}int main() {#ifndef ONLINE_JUDGE freopen("in.txt","r",stdin);// freopen("Out.txt", "w", stdout);#endif fgets(pic[0], N, stdin); int t; sscanf(pic[0], "%d", &t); while(t--) { solve(); } return 0;}
1572 uva
dfs 找环
图论模型
题目意思是:有n(n < 40000)种边上带标号的正方形。每条边上要么为一个大写字母后面跟着一个加号或减号,要么数字为00。 当且仅当两条边字母相同并且符号相反时,两条边可以连在一起(00不能跟任何一条边连在一起包括00)。假设输入的每种正方形都有无穷多种,而且可以旋转和翻转,你的任务是判断能否拼成一个无限大的结构。
这题我前两次做都失败了,第一次就知道要找环,也知道把字母看成点把正方形看成边,但是不知道怎么具体看。老是在初始化的时候就把A+ 和 A-…连接起来了。导致一直出错
分析:应该是这么连的,如果A- 和 B+在同一个正方形中, 那么A+ 就可以到 B+, B- 也可以到 A-。对一个正方形内所有的点进行这样连接,就可以构造出一个有向图,然后在跑dfs 做一次拓扑排序即可。细节在见代码:(因为wa了又没数据,所以自己写了个出数据的函数)
/* * * Author : Triose * Email : Triose@163.com * Update_time : 2016.6.12 * *///#include<bits/stdc++.h>#include<stdio.h>#include<iostream>#include<string>#include<string.h>#include<algorithm>#include<vector>#include<queue>#include<stack>#include<iterator>#include<math.h>#include<stdlib.h>#include<time.h>#include<map>#include<set>using namespace std;//#define ONLINE_JUDGE#define eps 1e-8#define fi first#define se second#define DB double#define LL long long#define inf 0x3f3f3f3f#define INF 0x7fffffff#define PR(a,b) pair<a,b>#define ds(t) int t; sf(t)#define enter putchar(10)const double E = exp(1.0);const double PI = acos(-1.0);#define INFL 0x3f3f3f3f3f3f3f3fLL#define mem(a,b) (memset((a),b,sizeof(a)))#define rep(i,a,b) for(int i = (a); i < (b); ++i)#define repe(i,a,b) for(int i = (a); i <= (b); ++i)#define sf(a) scanf("%d",&a)#define sfI(a) scanf("%I64d",&a)#define sfd(a,b) scanf("%d%d",&a,&b)#define sft(a,b,c) scanf("%d%d%d",&a,&b,&c)#define sfs(a) scanf("%s",a)#define pf(a) printf("%d\n",a)#define pfd(a,b) printf("%d %d\n",a,b)#define pfP(a) printf("%d %d\n",a.fi,a.se)#define pfs(a) printf("%s\n",a)#define pfI(a) printf("%I64d\n",a)template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }template<class T> inline T Min(T a, T b) { return a < b ? a : b; }template<class T> inline T Max(T a, T b) { return a > b ? a : b; }int n, m;#define N 55bool gra[N][N]; //图char str[10];int vis[N]; //访问状态数组bool has[N]; //记录点是否存在的数组int ID(int p) { //A+ ~ Z+ : [1, 26] | A- ~ Z- : [27, 52] if(str[p] == '0') return 0; int ans = str[p] - 'A' + 1; if(str[p + 1] == '-') ans += 26; return ans;}void link(int * id) { //连接点的函数 rep(i, 0, 4) { rep(j, 0, 4) { if(i == j || !id[i] || !id[j]) continue; int u = (id[i] < 27 ? id[i] + 26 : id[i] - 26); //把某一点的能连的合法点 和 正方形中的其他点连起来 比如说如果 A+ 和 B+ 在一个正方形中, 那么A- 和 B+ 是可达的 gra[u][id[j]] = true; } }}void Init() { mem(gra, false); mem(vis, 0); mem(has, false); //初始化图中的边、记录访问的数组、存在数组 while(n--) { sfs(str); int id[4]; rep(i, 0, 4) { id[i] = ID(i << 1); has[id[i]] = true; } link(id); //把正方形当做边,连接一些点(注意!该连接哪些点) }}bool dfs(int u) { //从u出发dfs遍历所有存在并且与u相连的点 if(vis[u] < 0) return true; //如果u正在被访问,那么就已经找到环了 if(vis[u] == 1) return false; //如果u已经被访问过,那么从u就不可能再找到环 vis[u] = -1; //标记u正在被访问 rep(v, 1, N) { if(has[v] && gra[u][v] && dfs(v)) return true; } vis[u] = 1; //执行到这步时就已经可以确定经过u找不到环 return false; //返回false}bool solve() { //如果找到环,返回true, 找不到返回false rep(i, 1, N) { if(has[i] && !vis[i] && dfs(i)) return true; } return false;}//void put_str() { //用来出测试数据// rep(i, 0, 4) {// int a, b;// a = (rand() % 26);// b = (a ? (rand() % 2) : 0);// str[i * 2] = (a == 0 ? '0' : 'A' + a);// str[i * 2 + 1] = (str[i * 2] == '0' ? '0' : (b == 0 ? '+' : '-'));// }// str[8] = '\0';// pfs(str);//}////void get_data() {// n = rand() % 20 + 1; //数据范围不要太大,不然难调试// pf(n);// rep(i, 0, n) put_str();// enter; enter;//}int main() {#ifndef ONLINE_JUDGE freopen("in.txt","r",stdin);// freopen("out.txt", "w", stdout);#endif// srand(time(NULL)); //随机数种子 while(~sf(n)) { Init(); if(solve()) pfs("unbounded"); else pfs("bounded"); }// rep(i, 1, 10) get_data(); //10组数据 return 0;}
bfs
入门:最短路
uva 816
挺麻烦的一题,主要是状态多出一维还要记录路线。
有一个最多包含 9 * 9 个交叉点的迷宫。输入起点、离开起点的朝向和终点,求一条最短路(多解时输出任意一个即可)。
解释样例:
SAMPLE
3 1 N 3 3
1 1 WL NR *
1 2 WLF NR ER *
1 3 NL ER *
2 1 SL WR NF *
2 2 SL WF ELF *
2 3 SFR EL *
0
NOSOLUTION
3 1 N 3 2
1 1 WL NR *
1 2 NL ER *
2 1 SL WR NFR *
2 2 SR EL *
0
END
Sample Output
SAMPLE
(3,1) (2,1) (1,1) (1,2) (2,2) (2,3) (1,3) (1,2) (1,1) (2,1)
(2,2) (1,2) (1,3) (2,3) (3,3)
NOSOLUTION
No Solution Possible
第一组:
起点(3, 1) 方向为N, 终点(3,3)。拐点(1,1)如果以W的朝向进入,则可以往左转(L),如果以N的朝向进入,则可以往右转(R),以此类推。
这题麻烦在状态,不过数据范围不大,9 * 9的格子, 4 * 4的方向(第一个4是进入方向,第二个4是出点的方向(出点的方向由进入方向和转向推出来))。
还有一个坑在每组数据有名字, 名字不能用%s或者cin>>title读入(因为可能有空格)。
输出格式也有控制,反正就是挺麻烦的一道题。做了半天。。
/* * * Author : Triose * Email : Triose@163.com * Update_time : 2016.6.12 * *///#include<bits/stdc++.h>#include<stdio.h>#include<iostream>#include<string>#include<string.h>#include<algorithm>#include<vector>#include<queue>#include<stack>#include<iterator>#include<math.h>#include<stdlib.h>#include<time.h>#include<map>#include<set>using namespace std;//#define ONLINE_JUDGE#define eps 1e-8#define inf 0x3f3f3f3f#define INF 0x7fffffff#define INFL 0x3f3f3f3f3f3f3f3fLL#define enter putchar(10)#define rep(i,a,b) for(int i = (a); i < (b); ++i)#define repe(i,a,b) for(int i = (a); i <= (b); ++i)#define mem(a,b) (memset((a),b,sizeof(a)))#define sf(a) scanf("%d",&a)#define sfI(a) scanf("%I64d",&a)#define sfd(a,b) scanf("%d%d",&a,&b)#define sft(a,b,c) scanf("%d%d%d",&a,&b,&c)#define sfs(a) scanf("%s",a)#define pf(a) printf("%d\n",a)#define pfd(a,b) printf("%d %d\n",a,b)#define pfp(a) printf("%d %d\n",a.fi,a.se)#define pfs(a) printf("%s\n",a)#define pfI(a) printf("%I64d\n",a)#define PR(a,b) pair<a,b>#define ds(t) int t; sf(t)#define fi first#define se second#define LL long long#define DB doubleconst double PI = acos(-1.0);const double E = exp(1.0);template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }template<class T> inline T Min(T a, T b) { return a < b ? a : b; }template<class T> inline T Max(T a, T b) { return a > b ? a : b; }int n, m;#define N 15char title[300];struct Point { int x, y; int dir; Point(int x_ = 0, int y_ = 0, int dir_ = 0) : x(x_), y(y_), dir(dir_) {} void operator = (const Point & t) { this->x = t.x; this->y = t.y; this->dir = t.dir; }};Point pv[N][N][5]; //保存某一点为(x, y, dir)的前一点bool vis[N][N][5]; //记录点(x, y)以朝向dir是否访问过bool can[N][N][5][5]; //记录点(x, y)以朝向dir到去向to能否通过int sx, sy, ex, ey; //起点终点坐标char dire[N];queue<Point> q; //用于bfsstack<Point> path; //用于输出答案const char * dirs = "NESW";map<char, int> turn;int dir_id(char ch) { return strchr(dirs, ch) - dirs; }int turn_id(char tn) { return turn[tn]; }int go[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; //NESWvoid mark(int px, int py) { //记录某个点怎么拐弯 while(sfs(dire)) { if(*dire == '*') return ; int len = strlen(dire); int from = dir_id(*dire); //进入时朝向 rep(i, 1, len) { int to = (from + turn_id(dire[i])) % 4; //到这个点时的去向 can[px][py][from][to] = true; } }}bool Init() { //读入并初始化 turn.clear(); while(!q.empty()) q.pop(); while(!path.empty()) path.pop(); turn['F'] = 0; turn['L'] = 3; turn['R'] = 1; gets(title); //这里注意 if(title[0] == '\0') gets(title); //这里也要注意 if(!strcmp(title, "END")) return false; mem(vis, false); mem(can, false); sfd(sx, sy); sfs(dire); sfd(ex, ey); int px, py; int to = dir_id(*dire); px = sx + go[to][0]; py = sy + go[to][1]; pv[px][py][to] = *(new Point(sx, sy, -1)); q.push(*(new Point(px, py, to))); while(sf(px) && px) { sf(py); mark(px, py); } return true;}bool bfs() { //普通bfs过程加上朝向这一状态 Point tmp; while(!q.empty()) { tmp = q.front(); q.pop(); vis[tmp.x][tmp.y][tmp.dir] = true; if(tmp.x == ex && tmp.y == ey) { path.push(tmp); return true; } rep(i, 0, 4) { //某一点的四个朝向 if(can[tmp.x][tmp.y][tmp.dir][i] && !vis[tmp.x + go[i][0]][tmp.y + go[i][1]][i] ) { vis[tmp.x + go[i][0]][tmp.y + go[i][1]][i] = true; pv[tmp.x + go[i][0]][tmp.y + go[i][1]][i] = tmp; q.push(*(new Point(tmp.x + go[i][0], tmp.y + go[i][1], i))); } } } return false;}void solve() { pfs(title); if(bfs()) { while(path.top().dir != -1) { //顺序要倒一下 path.push(pv[path.top().x][path.top().y][path.top().dir]); } int i = 0; //用于控制输出格式 while(!path.empty()) { i++; if(i % 10 == 1) printf(" "); printf(" (%d,%d)",path.top().x, path.top().y); if(i % 10 == 0) enter; path.pop(); } if(i % 10 != 0) enter; } else { pfs(" No Solution Possible"); }}int main() {#ifndef ONLINE_JUDGE freopen("in.txt","r",stdin);// freopen("Out.txt", "w", stdout);#endif while(Init()) { solve(); } return 0;}
bfs 最短路+颜色字典序最小
uva 1599 Ideal Path
题意,给出一个n个点m条边的无向图(2 <= n <= 10^5, 1 <= m <= 2 * 10^5),每条边上都涂有一种颜色。求从结点1到结点n的一条路经,使得经过的边数尽量少,在此前提下,经过边的颜色序列的字典序最小。一对结点间可能有多条边,一条边可能连接两个相同结点。输入保证结点1到结点n可达。颜色为1~10^9的整数。
在TLE了9发之后,我终于发现,就算给你5秒钟,用map里的count函数来判断两点间是否有边是不行的。count函数并不是O(1),不过时间效率类似二分,find函数稍快一点点(快不过一个数量级)。后来我决定不用map判断是否两点间有边,加了一个邻接表,直接遍历就行了(肯定有边的),用map记录颜色,这样就只需要在插入边的时候调用一下map的count函数判断是不是已经有了边,有了就不重复添加,更新颜色就行了(从而保存下来的每条边都是这两点间颜色最小的一条边),然后从终点往起点bfs一次求出最短距离,再从起点往终点bfs一次,求出最小字典序的路径并保存,输出即可。(别以为限制条件严格就不需要vis数组记录是否被访问过,如果不纪录据说复杂度是指数级(是真是假我也没仔细研究,反正亲测超时))
#include<bits/stdc++.h>using namespace std;//#define ONLINE_JUDGE#define eps 1e-8#define inf 0x3f3f3f3f#define INF 0x7fffffff#define INFL 0x3f3f3f3f3f3f3f3fLL#define enter putchar(10)#define rep(i,a,b) for(int i = (a); i < (b); ++i)#define repe(i,a,b) for(int i = (a); i <= (b); ++i)#define mem(a,b) (memset((a),b,sizeof(a)))#define sf(a) scanf("%d",&a)#define sfI(a) scanf("%I64d",&a)#define sfd(a,b) scanf("%d%d",&a,&b)#define sft(a,b,c) scanf("%d%d%d",&a,&b,&c)#define sfs(a) scanf("%s",a)#define pf(a) printf("%d\n",a)#define pfd(a,b) printf("%d %d\n",a,b)#define pfP(a) printf("%d %d\n",a.fi,a.se)#define pfs(a) printf("%s\n",a)#define pfI(a) printf("%I64d\n",a)#define ds(a) int a; sf(a)#define PR(a,b) pair<a,b>#define fi first#define se second#define LL long long#define DB doubleconst double PI = acos(-1.0);const double E = exp(1.0);template<class T> T gcd(T a, T b) {return b ? gcd(b, a % b) : a;}template<class T> T lcm(T a, T b) {return a / gcd(a, b) * b;}template<class T> inline T Min(T a, T b) {return a < b ? a : b;}template<class T> inline T Max(T a, T b) {return a > b ? a : b;}int n, m;#define N 100010vector<int> gra[N]; //邻接表map<int, int> cs[N]; //记录颜色int dis[N]; //记录到点n的距离int path[N]; //path[i]表示点1 到 点i 的颜色bool vis[N]; //扩散过一次之后不再扩散第二次void Ins(int s, int e, int col) { //邻接表的插入边 if(cs[s].count(e)) { //如果已经有一条边了,不再插入第二次(保留颜色最小的边) cs[s][e] = Min(cs[s][e], col); cs[e][s] = cs[s][e]; } else { //没有的话就直接插入(map的count函数效率并不是O(1)谨慎使用) gra[s].push_back(e); gra[e].push_back(s); cs[s][e] = col; cs[e][s] = col; }}void Init() { //初始化操作,学到了memset可以memset inf 这个在图论里非常常用 mem(dis, inf); mem(path, inf); mem(vis, false); repe(i, 1, n) { //邻接矩阵和颜色表的初始化 gra[i].clear(); cs[i].clear(); } int s, e, color; rep(i, 0, m) { sft(s, e, color); if(s != e) //直接忽略自环 Ins(s, e, color); }}void getdis() { //算出所有点到终点的距离,如果不可达则是inf int prev = n; dis[n] = 0; queue<int> q; q.push(n); //bfs的过程,保证距离最短 while(!q.empty()) { prev = q.front(); q.pop(); for(vector<int>::iterator it = gra[prev].begin(); it != gra[prev].end(); it++) { if(dis[*it] > dis[prev] + 1) { dis[*it] = dis[prev] + 1; q.push(*it); } } }}void getans() { int prev = 1; queue<int> q; q.push(1); while(!q.empty()) { //从1开始再bfs一次,沿着距离每次减1和颜色最小的边走 prev = q.front(); q.pop(); if(vis[prev]) continue; //加上这个后从tle变成了ac! 已经加入过队列一次的点不再加入 vis[prev] = true; //注意这里是取出的时候标记走过(这个标记的作用是避免重复走,而不是避免重复加入队列) int mcol = inf; //最小的颜色 for(vector<int>::iterator it = gra[prev].begin(); it != gra[prev].end(); it++) { if(dis[prev] - dis[*it] == 1 && cs[prev][*it] < mcol) { mcol = cs[prev][*it]; //求最小的颜色 } } for(vector<int>::iterator it = gra[prev].begin(); it != gra[prev].end(); it++) { if(dis[prev] - dis[*it] == 1 && cs[prev][*it] == mcol) { q.push(*it); //把所有颜色最小的并且符合距离减1的点再加入队列 path[dis[1] - dis[*it]] = Min(path[dis[1] - dis[*it]], cs[prev][*it]); //妙用! //dis[1] - dis[*it] 一定是大于等于0的(并且当*it等于1时等于0),所以可以用path[dis[1] - dis[*it]]记录某段(最小)颜色 } } }}void print() { pf(dis[1]); //最短路 repe(i, 1, dis[1]) { //总共会经过dis[1]条边,边的颜色已经记录在path里了,输出即可 printf("%d%c", path[i], i == dis[1] ? '\n' : ' '); }}int main() {#ifndef ONLINE_JUDGE freopen("in.txt", "r", stdin);// freopen("Out.txt", "w", stdout);#endif while(~sfd(n, m)) { Init(); //初始化 getdis(); //计算距离 getans(); //计算路径 print(); //输出 } return 0;}
心累的一题,不过这应该算是第一次正儿八经打邻接表。。。
- BFS-DFS
- BFS DFS
- DFS+BFS
- BFS DFS
- DFS BFS
- DFS & BFS
- BFS,DFS
- dfs bfs
- BFS&DFS
- DFS BFS
- BFS && DFS
- BFS DFS
- DFS & BFS
- DFS&BFS
- BFS&DFS
- dfs && bfs
- dfs bfs
- BFS/DFS
- POJ 3159 Candies
- [C/C++笔面试]自己实现Strlen,my_strlen最优解
- Miller-Rabin质数测试
- Python学习笔记之四——类型
- 97. Interleaving String
- dfs && bfs
- 使用三种循环遍历ArrayList类型数组。
- Vim十大必备插件
- chrome的插件Postman安装方法
- iOS 开发 用代码打开手电筒
- mysql中查询字段为null或者不为null
- Android网络编程(八)源码解析OkHttp后篇[复用连接池]
- json返回日期格式化的解决
- notification