PAT之图:遍历、最短路径dijkstra

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值