0 模板
邻接矩阵:(一般)
- 当N<1000
- 带权图
- 插删边 / 顶点
邻接表:
- 当N很大
- 题目明确说了,是稀疏图
邻接矩阵 模板
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1000+10;
int gra[N][N]; //邻接矩阵(1 or 0)或(data or INF)
bool visit[N];
int n;
void dfs(int v){
...
//
visit[v] = true;
for(int i=1; i<=n; i++){
if(visit[i]==false && gra[v][i]==1){
dfs(i);
}
}
}
void bfs(int v){
queue<int> Q;
Q.push(v);
visit[v] = true;
while(!Q.empty()){
int f = Q.front();
Q.pop();
for(int i=1; i<=n; i++){
if(visit[i]==false && gra[f][i]==1){
Q.push(i);
visit[i] = true;
}
}
}
}
int main(){
//freopen("in.txt", "r", stdin);
fill(gra[0], gra[0]+N*N, 0);
// fill(gra[0], gra[0] +N*N, INF);
fill(visit, visit+N, false);
//dfs
int cnt=0; //连通分量的个数
for(int i=1; i<=n; i++){
if(visit[i]==false){
dfs(i);
cnt++;
}
}
// bfs
int cnt=0; //连通分量的个数
for(int i=1; i<=n; i++){
if(visit[i]==false){
bfs(i);
cnt++;
}
}
//fclose(stdin);
return 0;
}
邻接表 模板
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int N=10000+10;
vector<vector<int> > gra;
// the nodes are numbered from 1 to n
// 否则:vector<int> gra[N];
int visit[N];
void dfs(int v){
...
//
visit[v] = true;
for(int i=0; i<gra[v].size(); i++){
int k = gra[v][i];
if(visit[gra[v][i]]==false){
dfs(gra[v][i]);
}
}
}
int main(){
freopen("in.txt", "r", stdin);
gra.resize(n+1); //从1~n
fill(visit, visit+N, false);
....
gra[a].push_back(b); //无向图
gra[b].push_back(a);
...
//
int cnt=0; //连通分量数
for(int i=1; i<=n; i++){
if(visit[i]==false){ //以i为顶点,遍历所有其它顶点(它能到达的)
dfs(i,1);
cnt++;
}
}
fclose(stdin);
return 0;
}
1 图的遍历
1.1 邻接矩阵/邻接表 + dfs/bfs
1013(25:邻接矩阵 + 求连通分量数 + dfs)
(1)题目
求最少添加几条边使图连通(= 连通分量数 - 1)
(2)代码
#include<cstdio>
#include<vector>
#include<algorithm>
#include<set>
using namespace std;
const int N=1000+10;
int gra[N][N]; //邻接矩阵(1 or 0)
bool visit[N];
int n;
void dfs(int v){
//
visit[v] = true;
for(int i=1; i<=n; i++){
if(visit[i]==false && gra[v][i]==1){
dfs(i);
}
}
}
int main(){
//freopen("in.txt", "r", stdin);
int m,k;
scanf("%d%d%d",&n, &m, &k);
fill(gra[0], gra[0]+N*N, 0);
for(int i=0; i<m; i++){
int a,b;
scanf("%d%d", &a, &b);
gra[a][b]=1; //无向图
gra[b][a]=1;
}
//
for(int i=0; i<k; i++){
fill(visit, visit+N, false);
int v;
scanf("%d", &v);
// 去除顶点v,就是标记为访问过
visit[v] = true;
//
int cnt=0;//连通分量数
for(int i=1; i<=n; i++){
if(visit[i]==false){
dfs(i);
cnt++;
}
}
printf("%d\n", cnt-1);
}
//fclose(stdin);
return 0;
}
(3)小结
- 使图连通时,最少添加边数 = 连通分量数 - 1
- 若删除边、顶点,用邻接矩阵
- 删除顶点:标记为访问过
visit[v] = true; - fill 二维数组
int a[N][N];
fill(a[0], a[0] + N*N, 0);
// 不是 fill(a, a + N*N, 0);
1021(25:邻接表 + 求连通分量数 + 每个顶点作为根节点的树的深度 + dfs)
(1)题目
求连通分量数 + 每个顶点作为根节点的树的深度
(2)代码
#include<cstdio>
#include<vector>
#include<algorithm>
#include<set>
using namespace std;
const int N=10000+10;
vector<vector<int> > gra;
// the nodes are numbered from 1 to n
// 否则:vector<int> gra[N];
int visit[N];
int depth[N]; //顶点i的深度
int temp_depth; //当前顶点作为根的深度
void dfs(int v, int level){
if(level > temp_depth){
temp_depth = level;
}
//
visit[v] = true;
for(int i=0; i<gra[v].size(); i++){
int k = gra[v][i];
if(visit[gra[v][i]]==false){
dfs(gra[v][i], level+1);
}
}
}
int main(){
//freopen("in.txt", "r", stdin);
int n;
scanf("%d",&n);
gra.resize(n+1); //从1~n
fill(visit, visit+N, false);
for(int i=0; i<n-1; i++){
int a,b;
scanf("%d%d", &a, &b);
gra[a].push_back(b); //无向图
gra[b].push_back(a);
}
//
int cnt=0; //连通分量数
for(int i=1; i<=n; i++){
if(visit[i]==false){
dfs(i,1); //i为根节点
cnt++;
}
}
if(cnt>=2) printf("Error: %d components",cnt);
else{
//每个节点作为根节点,都遍历一遍
for(int i=1; i<=n; i++){
fill(visit, visit+N, false);
temp_depth=0;
dfs(i,1);
depth[i] = temp_depth;
}
//
int max_high=0;
set<int> st;
for(int i=1; i<=n; i++){
if(depth[i] > max_high){
st.clear();
st.insert(i);
max_high = depth[i];
}else if(depth[i]==max_high){
st.insert(i);
}
}
set<int>::iterator it = st.begin();
for(; it!=st.end(); it++){
printf("%d\n", *it);
}
}
//fclose(stdin);
return 0;
}
1034(30:hash + 求带权图的每个连通分量的最大点权重和总权重 + dfs)
(1)题目
hash + 求带权图的每个连通分量的最大点权重和总权重
(2)代码
#include<cstdio>
#include<algorithm>
#include<map>
#include<string>
#include<iostream>
#include<set>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=2000+10;
int gra[N][N]; //邻接矩阵(data or INF)
bool visit[N];
int n;
int weight[N]; //顶点的权重
map<string, int> mp_head; //存储最终的所有head,按name排序
map<int , string> mp_itos; //得到string
map<string, int> mp_stoi; //得到int
int nameNum=1;
int string_to_int(string &str){
if(mp_stoi.find(str)==mp_stoi.end()){
mp_stoi[str] = nameNum;
mp_itos[nameNum] = str;
nameNum++;
}
return mp_stoi[str];
}
int gangNum; //每个连通分量的顶点数
int total; //每个连通分量的总权重
int head; //每个连通分量的head
void dfs(int v){
//
visit[v] = true;
gangNum++;
if(weight[v] > weight[head]){
head = v;
}
for(int i=1; i<nameNum; i++){
if(gra[v][i]!=INF){
total += gra[v][i];
if(visit[i]==false) {
dfs(i);
}
}
}
}
int main(){
//freopen("in.txt", "r", stdin);
int k;
scanf("%d%d",&n, &k);
fill(gra[0], gra[0]+N*N, INF);
fill(weight, weight+N, 0);
for(int i=0; i<n; i++){
string s1,s2;
cin>>s1>>s2;
int id1 = string_to_int(s1);
int id2 = string_to_int(s2);
int t;
scanf("%d", &t);
// 有向图
gra[id1][id2] = t;
weight[id1] += t;
weight[id2] += t;
}
fill(visit, visit+N, false);
//
for(int i=1; i<nameNum; i++){ //顶点:1~nameNum-1
if(visit[i]==false){
total = 0;
head = i;
gangNum = 0;
dfs(i);
if(gangNum>2 && total>k){
string name = mp_itos[head];
mp_head[name] = gangNum;
}
}
}
printf("%d\n", mp_head.size());
map<string, int>::iterator it;
for(it = mp_head.begin(); it!=mp_head.end(); it++){
printf("%s %d\n", it->first.c_str(), it->second);
}
//fclose(stdin);
return 0;
}
(3)小结
- 此题,hash存储字符串用的是map,字符串对应的id:从1到nameNum-1
map<int , string> mp_itos; //得到string
map<string, int> mp_stoi; //得到int
int nameNum=1;
-
map查找
if(mp.find(key) != mp.end()){
…
} -
段错误
(1)一个或两个测试点 段错误: N小了,数组要开大点
(2)多个测试点 段错误:写的代码数组越界
陷进,N到底为多大(最多顶点数),题目说 的是边最多1000条,N应该至少2000
1076(30:求单源点到每个顶点的层数 + bfs)
(1)
(2)
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1000+10;
int gra[N][N]; //邻接矩阵(1 or 0)
bool visit[N];
int n;
int L;
int level_id[N]; //每个节点的层数
int bfs(int v){
queue<int> Q;
Q.push(v);
visit[v] = true;
int cnt=0;
while(!Q.empty()){
int f = Q.front();
Q.pop();
for(int i=1; i<=n; i++){
if(visit[i]==false && gra[f][i]==1 && level_id[f]+1<=L){
level_id[i] = level_id[f]+1;
Q.push(i);
visit[i] = true;
cnt++;
}
}
}
return cnt;
}
int main(){
//freopen("in.txt", "r", stdin);
scanf("%d%d",&n, &L);
fill(gra[0], gra[0]+N*N, 0);
for(int i=1; i<=n; i++){
int t;
scanf("%d", &t);
for(int j=0; j<t; j++){
int a;
scanf("%d", &a);
gra[a][i] = 1; // 有向图
}
}
int k;
scanf("%d", &k);
for(int i=0; i<k; i++){
int id;
scanf("%d", &id);
fill(visit, visit+N, false);
fill(level_id, level_id+N, -1);
level_id[id] = 0;
printf("%d\n", bfs(id));
}
//fclose(stdin);
return 0;
}
(3)小结
- 根据遍历方向,设计边的方向
如, a fallows b,
gra[a][b]=1 ? or gra[b][a]=1 ?
对于b来说,求所有不超过L层的间接粉丝转发他微博的人数,即求所有其他节点到b的层数,因此,为了便于遍历,要逆向思维,把b作为根节点,求它到其他节点的层数
存gra[b][a] = 1
1.2 图论
1091(30:三维数组 + bfs)
(2)代码
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
int gra[1300][130][70]; // 70个二维:1300*130
bool visit[1300][130][70];
int m, n, L, T;
typedef struct Node{
int x,y,z;
}Node;
int X[6] = {1,-1,0,0,0,0};
int Y[6] = {0,0,1,-1,0,0};
int Z[6] = {0,0,0,0,1,-1};
//判断是否出边界
bool isIn(int x, int y, int z){
if(x>=0 && x<m && y>=0 && y<n && z>=0 && z<L) return true;
return false;
}
int bfs(int x, int y, int z){
Node node = {x,y,z};
queue<Node> Q;
Q.push(node);
visit[x][y][z] = true;
int cnt=0;
while(!Q.empty()){
Node f = Q.front();
Q.pop();
cnt++;
//6个方向遍历
for(int i=0; i<6; i++){
int tx = f.x + X[i];
int ty = f.y + Y[i];
int tz = f.z + Z[i];
if(isIn(tx,ty,tz) && visit[tx][ty][tz]==false && gra[tx][ty][tz]==1){
Node node_t = {tx, ty,tz};
Q.push(node_t);
visit[tx][ty][tz]=true;
}
}
}
if(cnt<T) cnt=0;
return cnt;
}
int main(){
//freopen("in.txt", "r", stdin);
scanf("%d%d%d%d",&m, &n, &L, &T);
for(int i=0; i<L; i++){
for(int j=0; j<m; j++){
for(int k=0; k<n; k++){
int t;
scanf("%d", &t);
gra[j][k][i] = t;
}
}
}
fill(visit[0][0], visit[0][0]+1300*130*70, false);
//
int num=0;
for(int i=0; i<L; i++){
for(int j=0; j<m; j++){
for(int k=0; k<n; k++){
if(visit[j][k][i] == false && gra[j][k][i]==1){
num += bfs(j,k,i);
}
}
}
}
printf("%d", num);
//fclose(stdin);
return 0;
}
(3)小结
- 顶点太多,用dfs递归内存会爆,因此用bfs
- 如果运行没有输出 或 评测结果为内存超限
则:bfs或dfs没有正确结束 - dfs / bfs中找到邻接点后,至少有2个判断
for(...)
if(visit[.]==false && gra[.][.]==1)
// or if(visit[.]==false && gra[.][.]!=INF)
1122(25:根据路径,判断环)
(1)
(2)代码
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N =300;
int gra[N][N];
int visit[N]; //统计访问几次
int n;
vector<int> vect;
int main(){
//freopen("in.txt", "r", stdin);
fill(gra[0], gra[0]+N*N, 0);
int m;
scanf("%d%d", &n, &m);
for(int i=0; i<m; i++){
int a,b;
scanf("%d%d", &a, &b);
gra[a][b] = gra[b][a]=1;
}
//
int k;
scanf("%d", &k);
for(int i=0; i<k; i++){
int t;
scanf("%d", &t);
vect.resize(t);
for(int j=0; j<t; j++){
scanf("%d", &vect[j]);
}
//
fill(visit, visit+N, 0);
int flag=0; //输出YES
for(int j=0; j<vect.size(); j++){
int v1 = vect[j];
if(j==vect.size()-1){
visit[v1] ++;
}else{
int v2 = vect[j+1];
visit[v1] ++;
if(gra[v1][v2]==0){
flag=1; //走不通
break;
}
if(visit[v1]==2) break;
}
}
if(flag==1) printf("NO\n");
else{
// 所有顶点visit[v]除了一个=2外,其余=1
int cnt=0;
for(int j=1; j<=n; j++){
if(visit[j]==1) {
continue;
}
if(visit[j]==2){
cnt++;
continue;
}
flag=1;
break;
}
if(cnt==1 && flag==0 && vect[0]==vect[vect.size()-1]){
printf("YES\n");
}else{
printf("NO\n");
}
}
vect.clear();
}
//fclose(stdin);
return 0;
}
(3)小结
- 明确什么情况输出 YES:(以下都满足)
1.走得通
2.头尾顶点相同
3.visit[头顶点]=2,其余顶点=1
1150(25:根据路径,判断环)
(2)代码
#include<cstdio>
#include<algorithm>
#include<vector>
const int INF=0x3f3f3f3f;
using namespace std;
const int N =300;
int gra[N][N];
int visit[N]; //统计访问几次
int n;
vector<int> vect;
int main(){
//freopen("in.txt", "r", stdin);
fill(gra[0], gra[0]+N*N, INF);
int m;
scanf("%d%d", &n, &m);
for(int i=0; i<m; i++){
int a,b,dis;
scanf("%d%d%d", &a, &b, &dis);
gra[a][b] = gra[b][a]=dis;
}
//
int best_path;
int best_dist=INF; //!!!
int k;
scanf("%d", &k);
for(int p=1; p<=k; p++){
fill(visit, visit+N, 0);
int len;
scanf("%d", &len);
for(int i=0; i<len; i++){
int t;
scanf("%d", &t);
vect.push_back(t);
}
//
int flag=0;
int dist=0;
for(int i=0; i<vect.size(); i++){
int v1 = vect[i];
visit[v1]++;
if(i!=vect.size()-1){
int v2 = vect[i+1];
if(gra[v1][v2]==INF){
flag=1;
dist=INF;
break;
}else{
dist += gra[v1][v2];
}
}
}
int flag2=0;
for(int i=1; i<=n; i++){
if(visit[i]==0){
flag=1;
break;
}
if(i!=vect[0] && visit[i]!=1){
flag2=1;
break;
}
}
if(vect[0]!=vect[vect.size()-1] || flag==1){
if(dist!=INF){
printf("Path %d: %d (Not a TS cycle)\n",p, dist);
}else{
printf("Path %d: NA (Not a TS cycle)\n",p);
}
}else{
if(visit[vect[0]]==2 && flag2==0){
printf("Path %d: %d (TS simple cycle)\n",p, dist);
}else{
printf("Path %d: %d (TS cycle)\n",p, dist);
}
if(dist<best_dist){
best_path = p;
best_dist = dist;
}
}
vect.clear();
}
printf("Shortest Dist(%d) = %d", best_path, best_dist);
//fclose(stdin);
return 0;
}
(3)小结
- 明确各种情况
(1)Not a TS cycle:(以下满足一个)
1.不通
2.存在visit[v]=0
3.头!=尾顶点
(2)TS simple cycle:
1.只有visit[头顶点]=2,其余=1
(3)Not a TS cycle
其余
1126(25:顶点的度+判断连通图)
(2)代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int N =600;
int gra[N][N];
bool visit[N]; //统计访问几次
int n;
int degree[N]; //顶点的度数
void dfs(int v){
visit[v] = true;
for(int i=1; i<=n; i++){
if(visit[i]==false && gra[v][i]==1){
dfs(i);
}
}
}
int main(){
//freopen("in.txt", "r", stdin);
fill(gra[0], gra[0]+N*N, 0);
int m;
scanf("%d%d", &n, &m);
for(int i=0; i<m; i++){
int a,b;
scanf("%d%d", &a, &b);
gra[a][b] = gra[b][a]=1;
}
//
int even_num=0, odd_num=0;
for(int i=1; i<=n; i++){
if(i!=1) printf(" ");
int deg=0;
for(int j=0; j<=n; j++){
if(gra[i][j]==1){
deg++;
}
}
if(deg%2==0) even_num++;
else odd_num++;
degree[i] = deg;
printf("%d", degree[i]);
}
printf("\n");
//
int cnt=0; //判断是不是连通图
fill(visit, visit+N, false);
for(int i=1; i<=n; i++){
if(visit[i]==false){
dfs(i);
cnt++;
}
}
//
if(cnt==1){
if(even_num==n){ //所有顶点度数都为偶
printf("Eulerian");
}else if(odd_num==2){ //只有2个顶点度数为奇
printf("Semi-Eulerian");
}else{
printf("Non-Eulerian");
}
}else{
printf("Non-Eulerian");
}
//fclose(stdin);
return 0;
}
(3)小结
- 看懂题目,不是要你证明(根据Eulerian path证明Eulerian),而是要你根据顶点的度判断是不是Eulerian
1142(1142:判断最大点集,任意两个点相连)
(2)代码
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N =300;
int gra[N][N];
int visit[N]; //统计访问几次
int n;
vector<int> vect;
bool isClique(){
for(int i=0; i<vect.size(); i++){
for(int j=i+1; j<vect.size(); j++){
if(gra[vect[i]][vect[j]]==0) return false;
}
}
return true;
}
int main(){
//freopen("in.txt", "r", stdin);
fill(gra[0], gra[0]+N*N, 0);
int ne;
scanf("%d%d", &n, &ne);
for(int i=0; i<ne; i++){
int a,b;
scanf("%d%d", &a, &b);
gra[a][b] = gra[b][a]=1;
}
//
int m;
scanf("%d", &m);
for(int i=0; i<m; i++){
fill(visit, visit+N, false);
int k;
scanf("%d", &k);
vect.resize(k);
for(int j=0; j<k; j++){
scanf("%d", &vect[j]);
visit[vect[j]] = true;
}
//
if(!isClique()){
printf("Not a Clique\n");
}else{
int flag=0;
for(int i=1; i<=n; i++){
if(visit[i]==false){
vect.push_back(i);
if(isClique()){
flag=1;
break;
}
vect.pop_back();
}
}
if(flag==0) printf("Yes\n");
else printf("Not Maximal\n");
}
vect.clear();
}
//fclose(stdin);
return 0;
}
(3)小结
- 完全图:
有向:m = n*(n-1)
无向:m = n*(n-1)/2 - adjacent.:指v1和v2有边,而不是有路径
2 最短路径
2.1 Dijkstra
模板
- 变量
int D[N]; 初始化:=INF 开始结点v =0
int path[N]; =-1 =-1
vector<int> path[N]; \ \
int PNUM[N]; =0 =1
int W[N]; =0 =weight[v]
- 步骤
第一步:写dijkstra():只求D[]和path[]
第二步:多条路径选1条,用dfs()从目的点开始,倒求,难点 - dijkstra()模板:顶点下标遍历,共4个for要修改
dijkstra() 模板
(1)求最短路径(1条)
int D[N]; //v到各个顶点的距离
int path[N]; //path[i]为i的前驱
//n个顶点,为0~n-1(一共4个for要修改)
void dijkstra(int v){
fill(visit, visit+N, false); //易遗漏!!!
fill(D, D+N, INF);
fill(path, path+N, -1);
for(int i=0; i<n; i++){
if(gra[v][i]!=INF){
D[i] = gra[v][i];
}
}
D[v] = 0; //!!!
path[v] = -1;
for(int k=0; k<n; k++){
int u=-1, min = INF; //到当前顶点的最短距离
for(int i=0; i<n; i++){ //在D[]中选择一条最短路径
if(visit[i]==false && D[i]<min){
u = i;
min = D[i];
}
}
if(u==-1) break;
visit[u] = true;
for(int i=0; i<n; i++){ // 更新其余顶点
if(visit[i]==false && gra[u][i]!=INF){
if(D[u]+gra[u][i] < D[i]) { // 1条最短路径(走i更短)
D[i] = D[u]+gra[u][i];
path[i] = u;
}
}
}
}
}
(2)多条最短路径
int D[N]; //v到各个顶点的距离
vector<int> path[N];
//n个顶点,为0~n-1(一共4个for要修改)
void dijkstra(int v){
fill(visit, visit+N, false);
fill(D, D+N, INF);
for(int i=0; i<n; i++){
if(gra[v][i]!=INF){
D[i] = gra[v][i];
}
}
D[v] = 0; //!!!
for(int k=0; k<n; k++){
int u=-1, min = INF; //到当前顶点的最短距离
for(int i=0; i<n; i++){ //在D[]中选择一条最短路径
if(visit[i]==false && D[i]<min){
u = i;
min = D[i];
}
}
if(u==-1) break;
visit[u] = true;
for(int i=0; i<n; i++){ // 更新其余顶点
if(visit[i]==false && gra[u][i]!=INF){
if(D[u]+gra[u][i] < D[i]) { // 1条最短路径(走i更短)
D[i] = D[u]+gra[u][i];
path[i].clear();
path[i].push_back(u);
}else if(D[u]+gra[u][i] == D[i]){ //多条最短路径(走i和走u一样)
path[i].push_back(u);
}
}
}
}
}
dfs() 模板
vector<int> bestPath, tempPath; //【倒存】:最后一个为 源点
int cntPath=0; //最短路径条数
void dfs(int v, int s){ //s为开始点
tempPath.push_back(v);
if(v==s){ //边界
cntPath++;
...
tempPath.pop_back();
return;
}
for(int i=0; i<path[v].size(); i++){
dfs(path[v][i], s);
}
tempPath.pop_back();
}
1003(25:多条最短路径 + dfs点权重)
(2)代码
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int INF=0x3f3f3f3f;
const int N =600;
int gra[N][N];
bool visit[N];
int n;
int weight[N]; //顶点权重
int D[N]; //v到各个顶点的距离
vector<int> path[N];
void dijkstra(int v){
fill(visit, visit+N, false);
fill(D, D+N, INF);
for(int i=0; i<n; i++){
if(gra[v][i]!=INF){
D[i] = gra[v][i];
}
}
D[v] = 0; //!!!
for(int i=0; i<n; i++){
int u=-1, min = INF; //到当前顶点的最短距离
for(int i=0; i<n; i++){ //在D[]中选择一条最短路径
if(visit[i]==false && D[i]<min){
u = i;
min = D[i];
}
}
if(u==-1) break;
visit[u] = true;
for(int i=0; i<n; i++){ // 更新其余顶点
if(visit[i]==false && gra[u][i]!=INF){
if(D[u]+gra[u][i] < D[i]) { // 1条最短路径(走i更短)
D[i] = D[u]+gra[u][i];
path[i].clear();
path[i].push_back(u);
}else if(D[u]+gra[u][i] == D[i]){ //多条最短路径(走i和走u一样)
path[i].push_back(u);
}
}
}
}
}
vector<int> bestPath, tempPath; //【倒存】:最后一个为 源点
int cntPath=0;
int maxWeight = -1;
void dfs(int v, int s){ //s为开始点
tempPath.push_back(v);
if(v==s){ //边界
cntPath++;
int sum=0;
for(int i=tempPath.size()-1; i>=0; i--){
int u = tempPath[i];
sum += weight[u];
}
if(sum > maxWeight){
maxWeight = sum;
bestPath = tempPath;
}
tempPath.pop_back();
return;
}
for(int i=0; i<path[v].size(); i++){
dfs(path[v][i], s);
}
tempPath.pop_back();
}
int main(){
//freopen("in.txt", "r", stdin);
fill(gra[0], gra[0]+N*N, INF);
int m, c1, c2;
scanf("%d%d%d%d", &n, &m, &c1, &c2);
for(int i=0; i<n; i++){
scanf("%d", &weight[i]);
}
for(int i=0; i<m; i++){
int a,b,dist;
scanf("%d%d%d", &a, &b, &dist);
gra[a][b] = gra[b][a]=dist;
}
//
dijkstra(c1);
//
dfs(c2, c1);
//
printf("%d %d", cntPath, maxWeight);
//fclose(stdin);
return 0;
}
1018(30:多条最短路径 + dfs)
(2)代码
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int INF=0x3f3f3f3f;
const int N =600;
int gra[N][N];
bool visit[N];
int n;
int weight[N]; //顶点权重
int CMAX;
int D[N]; //v到各个顶点的距离
vector<int> path[N];
void dijkstra(int v){
fill(D, D+N, INF);
for(int i=0; i<=n; i++){
if(gra[v][i]!=INF){
D[i] = gra[v][i];
}
}
D[v] = 0; //!!!
for(int i=0; i<=n; i++){ //n+1个顶点
int u=-1, min = INF; //到当前顶点的最短距离
for(int i=0; i<=n; i++){ //在D[]中选择一条最短路径
if(visit[i]==false && D[i]<min){
u = i;
min = D[i];
}
}
if(u==-1) break;
visit[u] = true;
for(int i=0; i<=n; i++){ // 更新其余顶点
if(visit[i]==false && gra[u][i]!=INF){
if(D[u]+gra[u][i] < D[i]) { // 1条最短路径(走i更短)
D[i] = D[u]+gra[u][i];
path[i].clear();
path[i].push_back(u);
}else if(D[u]+gra[u][i] == D[i]){ //多条最短路径(走i和走u一样)
path[i].push_back(u);
}
}
}
}
}
int bestPath[N];
int minSend, minBack;
void dfs(int v, int sumSend){
if(v==0){
}
for(int i=0; i<path[v].size(); i++){
int u = path[v][i];
dfs(u,sumSend+weight[u]); //v的前序
}
}
int main(){
freopen("in.txt", "r", stdin);
fill(gra[0], gra[0]+N*N, INF);
int sp,m;
scanf("%d%d%d%d", &CMAX, &n, &sp, &m);
for(int i=1; i<=n; i++){
scanf("%d", &weight[i]);
weight[i] = weight[i] - CMAX/2; //除了自身perfect外,剩余的(可能为负)
}
for(int i=0; i<m; i++){
int a,b,dist;
scanf("%d%d%d", &a, &b, &dist);
gra[a][b] = gra[b][a]=dist;
}
//
dijkstra(0);
// 调整
fclose(stdin);
return 0;
}
(3)小结
- dfs调整,从目的地sp开始,不是从根节点开始,逆向思维,倒着dfs
1030(30:多条最短路径 + dfs边权重)
(2)代码
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int INF=0x3f3f3f3f;
const int N =600;
int gra[N][N];
bool visit[N];
int n;
int cost[N][N]; //边权重
int D[N]; //v到各个顶点的距离
//n个顶点,为0~n-1(一共4个for要修改)
void dijkstra(int v){
fill(visit, visit+N, false);
fill(D, D+N, INF);
for(int i=0; i<n; i++){
if(gra[v][i]!=INF){
D[i] = gra[v][i];
}
}
D[v] = 0; //!!!
for(int i=0; i<n; i++){
int u=-1, min = INF; //到当前顶点的最短距离
for(int i=0; i<n; i++){ //在D[]中选择一条最短路径
if(visit[i]==false && D[i]<min){
u = i;
min = D[i];
}
}
if(u==-1) break;
visit[u] = true;
for(int i=0; i<n; i++){ // 更新其余顶点
if(visit[i]==false && gra[u][i]!=INF){
if(D[u]+gra[u][i] < D[i]) { // 1条最短路径(走i更短)
D[i] = D[u]+gra[u][i];
path[i].clear();
path[i].push_back(u);
}else if(D[u]+gra[u][i] == D[i]){ //多条最短路径(走i和走u一样)
path[i].push_back(u);
}
}
}
}
}
vector<int> bestPath, tempPath; //倒存:目的地,..,源点
int minCost = INF;
void dfs(int v, int s){ //s为开始点
tempPath.push_back(v);
if(v==s){ //边界
//求tempPath上的cost
int sum=0;
for(int i=tempPath.size()-1; i>0; i--){
int u = tempPath[i];
int pre = tempPath[i-1];
sum += cost[pre][u];
}
// 检查cost是否最小
if(sum < minCost){
minCost = sum;
bestPath = tempPath;
}
tempPath.pop_back();
return;
}
for(int i=0; i<path[v].size(); i++){
dfs(path[v][i], s);
}
tempPath.pop_back();
}
int main(){
//freopen("in.txt", "r", stdin);
fill(gra[0], gra[0]+N*N, INF);
int m, v1, v2;
scanf("%d%d%d%d", &n, &m, &v1, &v2);
for(int i=0; i<m; i++){
int a,b,dist,c;
scanf("%d%d%d%d", &a, &b, &dist, &c);
gra[a][b] = gra[b][a]=dist;
cost[a][b] = cost[b][a] = c;
}
//
dijkstra(v1);
//
dfs(v2, v1);
//
for(int i=bestPath.size()-1; i>=0; i--){
printf("%d ", bestPath[i]);
}
printf("%d %d", D[v2], minCost);
//fclose(stdin);
return 0;
}
拓扑排序
- 有向图G,是否存在拓扑排序?
存在: 则无环,进行拓扑排序
不存在,有环 - 对有向无环图进行拓扑排序:所有顶点的序列(可有多个序列)
系列中:所有顶点出现 且 仅1次; V1在V2的前面,则v1到V2必有路径
1146(25:判断序列是否为拓扑排序 + 顶点的入度)
顶点v:
(1)viisit[v] ==0
(2)入度 ==0
满足条件,则删除gra[v][i]的边,visit[v]
结束时:
所有顶点visit[]=1