图的遍历、或者答案的搜索。
两种最简单的搜索、遍历方法。
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 double
const 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 110
char 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 double
const 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 210
char 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 double
const 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 105
int 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 double
const 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 210
char 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 55
bool 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 double
const 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 15
char 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; //用于bfs
stack<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}}; //NESW
void 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 double
const 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 100010
vector<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;
}
心累的一题,不过这应该算是第一次正儿八经打邻接表。。。