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;}

心累的一题,不过这应该算是第一次正儿八经打邻接表。。。

0 0
原创粉丝点击