一、查找文献
模板题,BFS,DFS,此题节点数较大,只能使用邻接表储存。又由于本题需要求字典序最小的序列,那么就要将邻接表存储的结点按从小到大进行排序。那么我们可以开一个二维vector数组来储存。
#include<iostream>
#include<cstdio>
#include<queue>
#include <vector>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXSIZE 100010
vector < int > G[MAXSIZE];
queue< int > q;//bfs的队列
bool v[MAXSIZE];//记录是否访问过
int n, m;
void dfs(int x, int num)//x为节点,num为次数
{
v[x] = true;
cout << x << " ";
if (num == n)return;
for (int i = 0; i < G[x].size(); i++)
{
if (!v[G[x][i]])
dfs(G[x][i], num + 1);
}
}
void bfs(int x)
{
memset(v, false, sizeof(v));
q.push(x);
v[x] = true;
while (!q.empty())
{
int f = q.front();
q.pop();
cout << f << " ";
for (int i = 0; i < G[f].size(); i++)
{
if (!v[G[f][i]])
{
v[G[f][i]] = true;
q.push(G[f][i]);
}
}
}
}
int main()
{
cin >> n >> m;
int a,b;
for (int i = 1; i <= m; i++)
{
cin >> a >> b;
G[a].push_back(b);
}
for (int i = 1; i <= n; i++)
sort(G[i].begin(), G[i].end());//对邻接表排序
dfs(1, 0);
cout << endl;
bfs(1);
cout << endl;
}
二、图的遍历
按题目来每次考虑每个点可以到达点编号最大的点,不如考虑较大的点可以反向到达哪些点。循环从N到1,则每个点i能访问到的结点的A值都是i
#include<iostream>
#include<cstdio>
#include <vector>
using namespace std;
#define MAXSIZE 100010
vector < int > G[MAXSIZE];
int v[MAXSIZE];//记录是否访问过
int n, m;
void dfs(int x,int d)//x为节点,d为编号最大的点
{
if (v[x])return;
v[x] = d;
for (int i = 0; i < G[x].size(); i++)
{
if (!v[G[x][i]])
dfs(G[x][i],d);
}
}
int main()
{
cin >> n >> m;
int a,b;
for (int i = 1; i <= m; i++)
{
cin >> a >> b;
G[b].push_back(a);
}
for (int i = n; i >= 1; i--)
dfs(i, i);
dfs(1, 0);
for (int i = 1; i <= n; i++)
cout << v[i] << " ";
cout << endl;
}
三、P1113 杂务
一道拓扑排序题,
初始化队列,将入度为 0 的节点放入队列。
取出队首,遍历其出边,将能够到达的点入度减1,同时维护答案数组。
若在此时一个点的入度变为0,那么将其加入队列。
回到第二步,直到队列为空。
#include<iostream>
#include<cstdio>
#include<queue>
#include <vector>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXSIZE 10005
vector < int > g[MAXSIZE];//邻接表法
int l[MAXSIZE];//长度
int r[MAXSIZE];//入度
queue < int > q;//拓扑排序队列
int t[MAXSIZE];//各个节点最早结束时间
int main()
{
int n;
cin >> n;
for(int i=1;i<=n;i++)
{
int m,len,a;
cin >> m>>len;
l[m] = len;
while (cin >> a)
{
if (!a) break;
g[a].push_back(m);
r[m]++;
}
}
for (int i = 1; i <= n; i++)
{
if (r[i] == 0)
q.push(i);
}
t[1] = l[1];//第一个节点一定最小,且结束时间为持续时间
while (!q.empty())
{
int front = q.front();
q.pop();
for (int i = 0; i < g[front].size(); i++)
{
r[g[front][i]]--;
if (r[g[front][i]] == 0)
{
q.push(g[front][i]);
}
t[g[front][i]] = max(t[g[front][i]], t[front] + l[g[front][i]]);
}
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
ans = max(ans, t[i]);
}
cout << ans << endl;
return 0;
}
四、P4017 最大食物链计数
想要找到一条 最大食物链 ,则起始点入度要为0,终点出度要为0。于是有,既要记录入度,还要记录出度。
以第 i 个点结束的最大食物链的 数量 = 以指向第 i 个点的点结尾的最大食物链的 数量的和
由此呼之欲出,拓扑排序。最后将出度为0的点的食物链数量相加即可
#include<iostream>
#include<cstdio>
#include<queue>
#include <vector>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXSIZE 10005
#define mod 80112002
vector < int > g[MAXSIZE];//邻接表法
int in[MAXSIZE];//入度
int out[MAXSIZE];//出度
int num[MAXSIZE];//路径数量
queue < int > q;//拓扑排序队列
int main()
{
long int n, m,x,y;
cin >> n >> m;
for (long int i = 0; i < m; i++)
{
cin >> x >> y;
g[x].push_back(y);
out[x]++;
in[y]++;
}
for (int i = 1; i <= n; i++)
{
if (in[i] == 0)
{
q.push(i);
num[i] = 1;//令到其的路径数量为1
}
}
while (!q.empty())
{
int front = q.front();
q.pop();
for (int i = 0; i < g[front].size(); i++)
{
in[g[front][i]]--;
if (in[g[front][i]] == 0)
q.push(g[front][i]);
num[g[front][i]] = (num[front] + num[g[front][i]]) % mod;
}
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
if (out[i] == 0)
{
ans = (ans + num[i]) % mod;
}
}
cout << ans << endl;
}
五、P1807 最长路
对于一个有向无环图,可以采取广搜将结点依次搜索,记录当前结点的最长路并在循环过程中不断更新。
#include<iostream>
#include<cstdio>
#include<queue>
#include <vector>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXSIZE 10005
int max_length[MAXSIZE];//最长路径
queue < int > q;
int w[MAXSIZE][MAXSIZE];
int main()
{
int n, m,x,y,v;
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
cin >> x >> y>>v;
w[x][y] =max(w[x][y],v);
}
memset(max_length, -1, sizeof(max_length));
max_length[1] = 0;
q.push(1);
while (!q.empty())
{
int front = q.front();
q.pop();
for (int i = 1; i <= n; i++)
{
if (w[front][i] && (w[front][i] + max_length[front] > max_length[i]))
{
max_length[i] = w[front][i] + max_length[front];
q.push(i);
}
}
}
cout << max_length[n] << endl;
}
六、P1127 词链
题目大意:如果单词 X 的末字母与单词 YY的首字母相同,则 X 与 Y 可以相连成 X.Y。现在给你一些单词,请你找到字典序最小的词链,使得这些单词在词链中出现且仅出现一次。
这里是引用
#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
#define MAXSIZE 1005
string s[MAXSIZE];
map < char, int > s1, s2;//统计头尾
int flag=0;
int vis[MAXSIZE];//用来判断当前的字符串是否已经用过
string ans[MAXSIZE];//搜索时更新的数组
string now[MAXSIZE];//答案数组
int sum = 1;
int n;
void dfs(int x, int y)//x为要搜索的字符串下标,x为此时搜索次数
{
if (flag == 1)
return;
if (y == n)
{
flag = 1;
for (int i = 1; i <= sum; i++)
now[i] = ans[i];
return;
return;
}
for (int i = 1; i <= n; i++)
{
if (!vis[i])
{
if (s[x][s[x].length() - 1] == s[i][0])
{
ans[++sum] = s[i];
vis[i] = 1;
dfs(i, y + 1);
sum--;
vis[i] = 0;
}
}
}
}
int main()
{
memset(vis, 0, sizeof(vis));
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> s[i];
s1[s[i][0]]++;
s2[s[i][s[i].length() - 1]]++;//尾部
}
int start = 1;
sort(s + 1, s + 1 + n);//字典序
for (int i = 1; i <= n; i++)
{
if (s1[s[i][0]] - s2[s[i][0]]==1)//当前字符串第一个字母前后出现差值为1
{
start = i;
break;
}
}
vis[start] = 1;
ans[sum] = s[start];
dfs(start, 1);
if (flag == 0)
cout << "***" << endl;
else
{
for (int i = 1; i <=n; i++)
{
if (i!=1)
cout << ".";
cout << now[i];
}
}
}
七、P2853 [USACO06DEC]Cow Picnic S
从k个奶牛分别dfs,用num[i]表示第i个牧场被遍历过多少次,最后只有num[i]==k的牧场满足条件。无权的有向图也可以用vector存储。
#include<iostream>
#include<cstdio>
#include <vector>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXSIZE 10005
int num[MAXSIZE];//保存次数
vector < int > g[MAXSIZE];
int a[MAXSIZE];
int vis[MAXSIZE];
void dfs(int x)
{
vis[x] = 1;
num[x]++;
for (int i = 0; i < g[x].size(); i++)
{
if (!vis[g[x][i]])
{
dfs(g[x][i]);
}
}
}
int main()
{
int k, n, m;
cin >> k>> n >> m;
for (int i = 1; i <= k; i++)
{
cin >> a[i];//储存位置
}
for (int i = 1; i <= m; i++)
{
int x, y;
cin >> x >> y;
g[x].push_back(y);
}
for (int i = 1; i <= k; i++)
{
memset(vis, 0, sizeof(vis));
dfs(a[i]);
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
if (num[i] == k)
{
ans++;
}
}
cout << ans << endl;
}
八、P1363 幻象迷宫
某一个点可以走到它在别的图的映射的它自己就行(即是映射地址一样,但真实地址不一样),但走到它自己图上的它自己就不行。重点就在于此。
#include<iostream>
#include<cstring>
using namespace std;
#define MAXSIZE 1505
const int dx[5] = { 0,-1,0,1,0 };
const int dy[5] = { 0,0,1,0,-1 };
char g[MAXSIZE][MAXSIZE];
int used[MAXSIZE][MAXSIZE][3];
//0记录是否来过,1记录上一次到达时真实x坐标,2记录真实y坐标
int flag=0;
int n, m;
void dfs(int x, int y, int nx, int ny)//x,y为映射坐标,nx,ny为真实坐标
{
//某一个点可以走到它在别的图的它自己就行,就代表当记录的真实坐标两个不一致,则认为走出
if (used[x][y][0] == 1 && (used[x][y][1] != nx || used[x][y][2] != ny))
flag = 1;
if (used[x][y][0])
return;
//走过之后在记录
if (flag)
return;
used[x][y][0] = 1;
used[x][y][1] = nx ;
used[x][y][2] = ny ;
for (int i = 1; i <= 4; i++)
{
int tox, toy;
tox = (x + dx[i] + n) % n;
toy = (y + dy[i] + m) % m;
if (g[tox][toy] != '#')
{
dfs(tox, toy, nx + dx[i], ny + dy[i]);
}
}
}
void work()
{
flag = 0;
int sx=0, sy=0;//记录开始
memset(g, 0, sizeof(g));
memset(used, 0, sizeof(used));
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
cin >> g[i][j];
if (g[i][j] == 'S')
{
sx = i;
sy = j;
}
}
}
dfs(sx, sy, sx, sy);
if (flag)
cout << "Yes" << endl;
else
cout << "No" << endl;
}
int main()
{
while (cin >> n >> m)
work();
}
九、排序
输出要求输出到第几次就行了,或者不行了,就说明我们每建一条边就需要一次拓扑排序。
有三种情况,1:有稳定的拓扑排序,2:有环,3:无稳定拓扑排序
有稳定拓扑排序说明拓扑排序的层数是n,看最大层数ans是否等于n。
如果拓扑排序没能遍历所有的点,就说明存在一个环。也就是下面的sum!=char_mumber.size()。char_mumber是用来存储目前元素(点)个数的。如果前两种都不是,那肯定就是最后一种了
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
#include <set>
#include <bits/stdc++.h>
using namespace std;
#define MAXSIZE 28
vector < int > g[MAXSIZE];
int in[MAXSIZE];//入度保存
int ans=0;//层数保存
int n,m;
set < int > char_number;
int sum = 0;//参与拓扑排序的字母数量
int k;//此时的位置
void make_in();
struct node
{
int val;//层数
int spot;//点
node(int spot = 0, int val = 0) :spot(spot), val(val) {}
//结构体的重构
};
void make()
{
make_in();
queue< int > q;
for (int i = 0; i < 26; i++)
{
if (in[i] == 0 && char_number.count(i))
{
q.push(i);
cout << char(i + 'A');
}
}
while (!q.empty())
{
int front = q.front();
q.pop();
for (int i = 0; i < g[front].size(); i++)
{
in[g[front][i]]--;
if (in[g[front][i]]== 0)
{
q.push(g[front][i]);
cout << char(g[front][i] + 'A');
}
}
}
}
void test()
{
make_in();
queue< node > q;
for (int i = 0; i < 26; i++)
{
if (in[i] == 0 && char_number.count(i))
{
q.push(node(i,1));
sum++;
}
}
while (!q.empty())
{
int front = q.front().spot;
int v = q.front().val;
q.pop();
for (int i = 0; i < g[front].size(); i++)
{
in[g[front][i]]--;
if (in[g[front][i]]==0)
{
sum++;
q.push(node(g[front][i], v + 1));
ans = max(ans, v + 1);
}
}
}
if (ans == n)
{
cout << "Sorted sequence determined after " <<k<< " relations: ";
make();
cout << ".";
exit(0);
}
if (sum != char_number.size())
{
cout << "Inconsistency found after "<< k<<" relations." ;
exit(0);
}
}
void make_in()//计算入度
{
memset(in, 0, sizeof(in));
for (int i = 0; i < 26; i++)
{
for (int j = 0; j < g[i].size(); j++)
{
in[g[i][j]]++;
}
}
}
int main()
{
cin >> n >> m;
string s;
for (int i = 1; i <=m; i++)
{
cin >> s;
g[s[0] - 'A'].push_back(s[2] - 'A');
in[s[0] - 'A']++;
char_number.insert(s[0] - 'A');
char_number.insert(s[2] - 'A');
//每次要清楚ans,sum计数和用k标记此时的位置
ans = 0;
sum=0;
k = i;
test();
}
cout << "Sorted sequence cannot be determined.";
}
十、P1983 车站分级
因为火车都要停靠比它高级(大于等于)的车站,所以其它不停的就是比它级别小(小于)的车站,现在求最高等级的车站是几级。在所有不停的站的级别小于停靠的站的情况下,我们可以做出一张图。最后利用拓扑的思想,一层一层的删点删线,统计下分了多少层
#include<iostream>
#include<cstring>
#include<vector>
#include <set>
using namespace std;
#define maxsize 1505
int n, m,k,a[maxsize];
bool v[maxsize];//记录路上的车站是否停过
int g[maxsize][maxsize];
int in[maxsize];
bool vis[maxsize];//记录被删的点
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
cin >> k;
memset(v, 0, sizeof(v));
for (int j = 1; j <= k; j++)
{
cin >> a[j];
v[a[j]] = true;
}
for (int j = a[1]; j <= a[k]; j++)
{
if (!v[j])
{//以此加边
for (int s = 1; s <= k; s++)
{
if (!g[j][a[s]])
{
g[j][a[s]] = 1;
in[a[s]]++;//此点入度加一,表示从小到大
}
}
}
}
}
int ans = 0,top;
int stack[maxsize];
do//统计多少层
{
top = 0;
for (int i = 1; i <= n; i++)
{
if (in[i] == 0 && !vis[i])
{
stack[++top] = i;
vis[i] = true;
}
}
for (int i = 1; i <= top; i++)
{
for (int j = 1; j <= n; j++)
{
if (g[stack[i]][j] == 1)
{
in[j]--;
g[stack[i]][j] = 0;
}
}
}
ans++;
} while (top);
cout << ans-1 << endl;
}