图论基础杂选

图论基础杂选

UVA 816

题目要求

在普通的迷宫问题上加入了转弯转向的问题 特定的位置只能在特定的转向里选择

参考AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
struct Node
{
int r, c, dir; // 位于(r,c)朝向dir(0~3表示四个方向N, E, S, W)
Node(int r=0, int c=0, int dir=0):r(r),c(c),dir(dir) {}
};
const int maxn = 10;
const char* dirs = "NESW"; // 顺时针旋转。
const char* turns = "FLR";//“三种转弯方式”。
int has_edge[maxn][maxn][4][3];// 表示当前状态(r,c,dir),是否可以沿着转弯方向[trun]行走。
int d[maxn][maxn][4]; //表示初始状态到(r,c,dir)的最短路长度。
Node p[maxn][maxn][4]; //同时用p[r][c][dir]保存了状态(r, c, dir)在BFS树中的父结点。
int r0, c0, dir, r1, c1, r2, c2;
//把四个方向和3种“转弯方式”编号0~3和0~2.
int dir_id(char c) { return strchr(dirs, c) - dirs; }
int turn_id(char c) { return strchr(turns, c) - turns; }
//用于转弯。
const int dr[] = {-1, 0, 1, 0};
const int dc[] = {0, 1, 0, -1};
Node walk(const Node& u, int turn)
{
int dir = u.dir; //直行, 方向不变
if(turn == 1) dir = (dir + 3) % 4; // 逆时针 ,转向
if(turn == 2) dir = (dir + 1) % 4; // 顺时针 ,转向
return Node(u.r + dr[dir], u.c + dc[dir], dir);//下一步可能的状态
}
//判断是否出界
bool inside(int r, int c)
{
return r >= 1 && r <= 9 && c >= 1 && c <= 9;
}
//读取r0,c0,dir,并计算出r1,c1, 然后读入has_edge数组。
bool read_case()
{
char s[99], s2[99];
if(scanf("%s%d%d%s%d%d", s, &r0, &c0, s2, &r2, &c2) != 6)
return false;
printf("%s\n", s);
dir = dir_id(s2[0]);
r1 = r0 + dr[dir];
c1 = c0 + dc[dir];
memset(has_edge, 0, sizeof(has_edge));
for(;;)
{
int r, c;
scanf("%d", &r);
if(r == 0)
break;
scanf("%d", &c);
while(scanf("%s", s) == 1 && s[0] != '*')
{
for(int i = 1; i < strlen(s); i++)
has_edge[r][c][dir_id(s[0])][turn_id(s[i])] = 1;
}
}
return true;
}
void print_ans(Node u)
{
// 从目标结点逆序追溯到初始结点。
vector<Node> nodes;
for(;;)
{
nodes.push_back(u);
if(d[u.r][u.c][u.dir] == 0)
break;
u = p[u.r][u.c][u.dir];
}
nodes.push_back(Node(r0, c0, dir));
//打印解, 每行 10 个。
int cnt = 0;
for(int i = nodes.size()-1; i >= 0; i--)
{
if(cnt % 10 == 0)
printf(" ");
printf(" (%d,%d)", nodes[i].r, nodes[i].c);
if(++cnt % 10 == 0)
printf("\n");
}
if(nodes.size() % 10 != 0)
printf("\n");
}
//BFS主过程。
void solve()
{
queue<Node> q;
memset(d, -1, sizeof(d));
Node u(r1, c1, dir);
d[u.r][u.c][u.dir] = 0;
q.push(u);
while(!q.empty())
{
Node u = q.front(); q.pop();
if(u.r == r2 && u.c == c2)
{ print_ans(u); return; }//到达目的地
for(int i = 0; i < 3; i++)
{//所有可能的转向,(直行,逆时针转, 顺时针转)
Node v = walk(u, i); //下一步的状态
if(has_edge[u.r][u.c][u.dir][i] && inside(v.r, v.c) && d[v.r][v.c][v.dir] < 0) {//分别判断
//从这一步是否可以达到下一步,下一步是否出界, 下一步是否被走过(同方向)。
d[v.r][v.c][v.dir] = d[u.r][u.c][u.dir] + 1;//最短长度加 1.
p[v.r][v.c][v.dir] = u;//记录父结点。
q.push(v);
}
}
}
printf(" No Solution Possible\n");//走了所有可以走的可能, 无法到达终点。
}
int main()
{
while(read_case())
{
solve();
}
return 0;
}

思路

详见算法竞赛入门经典(二)P165-167

拓扑排序

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <iostream>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
vector<int>e[20];
queue<int>q;
int degree[10];
int n;
int u,v;
void find_degree()
{
memset(degree,0,sizeof(degree));
for(int i=1;i<=n;i++)
for(int j=0;j<e[i].size();j++)
degree[e[i][j]]++;
for(int i=1;i<=n;i++)
cout<<i<<":"<<degree[i]<<endl;
}
void toposort()
{
int count=0;
for(int i=1;i<=n;i++)
if(degree[i]==0)
q.push(i);
while(q.size())
{
int id=q.front();
q.pop();
cout<<id<<" ";
count++;
for(int i=0;i<e[id].size();i++)
{
degree[e[id][i]]--;
if(degree[e[id][i]]==0)
q.push(e[id][i]);
}
}
cout<<endl;
cout<<count<<endl;
if(count<n)
cout<<"do not exist toposort!"<<endl;
}
int main(int argc, char** argv)
{
freopen("input.txt","r",stdin);
cin>>n;
while(cin>>u>>v)
{
e[u].push_back(v);
}
find_degree();
toposort();
return 0;
}
//读入数据
9
1 3
1 8
2 5
2 4
2 3
3 4
4 6
4 7
5 6
8 9
9 7
//若数据做如下修改 则输出不存在toposort
9
1 3
1 8
2 5
2 4
3 2
4 3
4 6
4 7
5 6
8 9
9 7

作用

toposort可用于检查所给图是否为有向无环图
只有有向无环图才能输出拓扑序列

单向TSP问题–UVA 116

题目要求

给一个m行n列(m≤10,n≤100)的整数矩阵 从第一列任何位置出发每次往右 右上或右下走一格 最终达到最后一列。要求经过的整数之和最小
整个矩阵是环形的 即第一行的上一行是最后一行 最后一行的下一行是第一行 输出路径上每列的行号 多解时输出字典序最小的。

参考AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
using namespace std;
int maps[11][101];
int smap[11][101];
int fath[11][101];
int main()
{
int n,m;
while (~scanf("%d%d",&n,&m))
{
for (int i = 1 ; i <= n ; ++ i)
for (int j = 1 ; j <= m ; ++ j)
scanf("%d",&maps[i][j]);
memset(smap, 0, sizeof(smap));
for (int i = m ; i >= 1 ; -- i)
for (int j = 1 ; j <= n ; ++ j)
{
smap[j][i] = smap[j][i+1]+maps[j][i];
fath[j][i] = j;
if (j > 1 && smap[j][i] >= smap[j-1][i+1]+maps[j][i])
{
smap[j][i] = smap[j-1][i+1]+maps[j][i];
fath[j][i] = j-1;
}
if (j == n && smap[j][i] >= smap[1][i+1]+maps[j][i])
{
smap[j][i] = smap[1][i+1]+maps[j][i];
fath[j][i] = 1;
}
if (j < n && smap[j][i] > smap[j+1][i+1]+maps[j][i])
{
smap[j][i] = smap[j+1][i+1]+maps[j][i];
fath[j][i] = j+1;
}
if (j == 1 && smap[j][i] > smap[n][i+1]+maps[j][i])
{
smap[j][i] = smap[n][i+1]+maps[j][i];
fath[j][i] = n;
}
}
int spa = 1;
for (int i = 2 ; i <= n ; ++ i)
if (smap[spa][1] > smap[i][1])
spa = i;
int min = smap[spa][1];
for (int i = 1 ; i <= m ; ++ i)
{
if (i < m)
printf("%d ",spa);
else
printf("%d\n%d\n",spa,min);
spa = fath[spa][i];
}
}
return 0;
}

思路

dp,动态规划。因为要字典序最小,所以采用从右向左的方式dp;
状态:f(i,j)表示走到(i,j)的最小和,则有转移方程:
f(i,j)= min(f(i+1,j+1),f(i,j+1),f(i-1,j+1));
记录路径输出即可。
逆向dp保证字典序最小(后继最小),正向能保证每点前驱最小。
因为保证逆序 所以从最后一列开始往前推 fath继续前驱结点
最后找到第一列的最小元素 即为开头 利用fath数组正向输出即可

文章目录
  1. 1. 图论基础杂选
    1. 1.1. UVA 816
      1. 1.1.1. 题目要求
      2. 1.1.2. 参考AC代码
      3. 1.1.3. 思路
    2. 1.2. 拓扑排序
      1. 1.2.1. 参考代码
      2. 1.2.2. 作用
    3. 1.3. 单向TSP问题–UVA 116
      1. 1.3.1. 题目要求
      2. 1.3.2. 参考AC代码
      3. 1.3.3. 思路
|