算法笔记Codeup、pat刷题记录(含个人笔记)第十章

2021算法笔记Codeup、pat刷题记录

《算法笔记》10.3小节——图算法专题->图的遍历

Codeup

第一题

坑题,没告诉数据大小,一开始开了100010的vector大小运行错误,然后也没讲是有向图还是无向图,用有向图写也错了,虽然连通分支好像意味着是有向图。
这里使用了set记录图中的结点,因为set是递增的且不会有重复元素,因此可以很舒服的写出代码
内存 23428KB
耗时 274ms

#include<cstdio>
#include<vector>
#include<set>
using namespace std;
const int maxn = 500010;
vector<int> Adj[maxn];
set<int> st;
bool vis[maxn] = { false };

void CreateGraph() {
	int a, b;
	while (scanf("%d %d", &a, &b) != EOF) {
		st.insert(a);
		st.insert(b);
		if (a != b) {
			Adj[a].push_back(b);
			Adj[b].push_back(a);
		}
		else {
			Adj[a].push_back(b);
		}
	}
}

void DFS(int u) {
	vis[u] = true;
	for (int i = 0;i < Adj[u].size();++i) {
		int v = Adj[u][i];
		if (vis[v] == false) {
			DFS(v);
		}
	}
}

int main() {
	int cnt = 0;
	CreateGraph();
	for (set<int>::iterator it = st.begin();it != st.end();++it) {
		if (vis[*it] == false && Adj[*it].size() > 0) {
			DFS(*it);
			++cnt;
		}
	}
	printf("%d\n", cnt);
	return 0;
}

连通图

#include<cstdio>
#include<vector>
using namespace std;
const int maxn = 1010;
int n, m;
vector<int> Adj[maxn];
bool vis[maxn] = { false };

void DFS(int u) {
	vis[u] = true;
	for (int i = 0;i < Adj[u].size();++i) {
		int v = Adj[u][i];
		if (vis[v] == false) {
			DFS(v);
		}
	}
}

int main() {
	while (scanf("%d %d", &n, &m), n) {
		for (int i = 0;i < m;++i) {
			int a, b;
			scanf("%d %d", &a, &b);
			Adj[a].push_back(b);
			Adj[b].push_back(a);
		}
		int cnt = 0;
		for (int i = 1;i <= n;++i) {
			if (vis[i] == false) {
				DFS(i);
				++cnt;
			}
		}
		if (cnt == 1) printf("YES");
		else printf("NO");
		printf("\n");
		for (int i = 1;i <= n;++i) {
			Adj[i].clear();
		}
	}
	return 0;
}

配套实战指南

PAT A1013 Battle Over Cities

这题最关键的点在于删除结点并不一定要真的删除,而是遍历到删除结点时退出即可。
这里看maxn=1010不大所以使用了邻接矩阵的写法。

#include<cstdio>
#include<cstring>
const int maxn = 1010;
bool G[maxn][maxn] = { false }, vis[maxn] = { false };
int n, m, k, ban;

void DFS(int u, int pre) {//pre表示u的父结点,由于是无向图因此不能走回头路
	if (u != ban) {
		vis[u] = true;
		for (int i = 1;i <= n;++i) {
			if (G[u][i] == true && vis[i] == false && i != pre) {
				DFS(i, u);
			}
		}
	}
	else return;

}

int main() {
	scanf("%d %d %d", &n, &m, &k);
	for (int i = 0;i < m;++i) {
		int a, b;
		scanf("%d %d", &a, &b);
		G[a][b] = true;
		G[b][a] = true;
	}
	for (int i = 0;i < k;++i) {
		int cnt = 0;
		scanf("%d", &ban);
		memset(vis, false, sizeof(vis));
		for (int j = 1;j <= n;++j) {
			if (vis[j] == false && j != ban) {
				DFS(j, -1);
				++cnt;
			}
		}
		printf("%d\n", cnt - 1);
	}

	return 0;
}

邻接表写法
改的不多

#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn = 1010;
bool vis[maxn] = { false };
vector<int> Adj[maxn];
int n, m, k, ban;

void DFS(int u, int pre) {//pre表示u的父结点,由于是无向图因此不能走回头路
	if (u != ban) {
		vis[u] = true;
		for (int i = 0;i < Adj[u].size();++i) {
			int v = Adj[u][i];
			if (vis[v] == false && Adj[u][i] != pre) {
				DFS(v, u);
			}
		}
	}
	else return;

}

int main() {
	scanf("%d %d %d", &n, &m, &k);
	for (int i = 0;i < m;++i) {
		int a, b;
		scanf("%d %d", &a, &b);
		Adj[a].push_back(b);
		Adj[b].push_back(a);
	}
	for (int i = 0;i < k;++i) {
		int cnt = 0;
		scanf("%d", &ban);
		memset(vis, false, sizeof(vis));
		for (int j = 1;j <= n;++j) {
			if (vis[j] == false && j != ban) {
				DFS(j, -1);
				++cnt;
			}
		}
		printf("%d\n", cnt - 1);
	}

	return 0;
}

并查集写法

#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
const int maxn = 1010;
vector<int> Adj[maxn];
int father[maxn];
bool vis[maxn];

int findFather(int x) {
	int a = x;
	while (x != father[x]) x = father[x];
	while (a != father[a]) {
		int z = a;
		a = father[a];
		father[z] = x;
	}
	return x;
}

void Union(int a, int b) {
	int faA = findFather(a);
	int faB = findFather(b);
	if (faA != faB) {
		father[faA] = faB;
	}
}

void init() {
	for (int i = 1;i <= maxn;++i) {
		father[i] = i;
	}
	memset(vis, false, sizeof(vis));
}
int n, m, k;
int main() {
	scanf("%d %d %d", &n, &m, &k);
	for (int i = 0;i < m;++i) {
		int a, b;
		scanf("%d %d", &a, &b);
		Adj[a].push_back(b);
		Adj[b].push_back(a);
	}
	int ban;
	for (int q = 0;q < k;++q) {
		scanf("%d", &ban);
		init();
		for (int i = 1;i <= n;++i) {
			for (int j = 0;j < Adj[i].size();++j) {
				int u = i, v = Adj[i][j];
				if (u != ban && v != ban) Union(u, v);
			}
		}
		int cnt = 0;
		for (int i = 1;i <= n;++i) {
			if (i == ban) continue;
			int fa_i = findFather(i);
			if (vis[fa_i] == false) {
				++cnt;
				vis[fa_i] = true;
			}
		}
		printf("%d\n", cnt - 1);
	}

	return 0;
}

PAT A1021 Deepest Root

书上求最深结点的方法,值得牢记,用并查集判断连通图的个数很方便,值得学习,用set存储最后的数据可以减少代码量,因为set中的元素是递增排序且无重复的。

#include<cstdio>
#include<vector>
#include<set>
using namespace std;
const int maxn = 10010;
vector<int> G[maxn];

int father[maxn];
int n;
bool hashTable[maxn] = { false };
int findFather(int x) {
	int a = x;
	while (x != father[x]) x = father[x];
	while (a != father[a]) {
		int z = a;
		a = father[a];
		father[z] = x;
	}
	return x;
}

void Union(int a, int b) {
	int faA = findFather(a);
	int faB = findFather(b);
	if (faA != faB) {
		father[faA] = faB;
	}
}

void init() {
	for (int i = 1;i <= n;++i) {
		father[i] = i;
	}
}

int calBlock() {
	int Block = 0;
	for (int i = 1;i <= n;++i) {
		hashTable[findFather(i)] = true;
	}
	for (int i = 1;i <= n;++i) {
		Block += hashTable[i];
	}
	return Block;
}

int maxH = 0;
set<int> ans, temp;
void DFS(int u, int Height, int pre) {
	if (Height > maxH) {
		temp.clear();
		temp.insert(u);
		maxH = Height;
	}
	else if (Height == maxH) {
		temp.insert(u);
	}
	for (int i = 0;i < G[u].size();++i) {
		if (G[u][i] != pre) {
			DFS(G[u][i], Height + 1, u);
		}
	}
}

int main() {
	scanf("%d", &n);
	init();
	for (int i = 0;i < n - 1;++i) {
		int a, b;
		scanf("%d %d", &a, &b);
		G[a].push_back(b);
		G[b].push_back(a);
		Union(a, b);
	}
	int Block = calBlock();
	if (Block == 1) {
		DFS(1, 1, -1);
		ans = temp;
		set<int>::iterator it = temp.begin();
		DFS(*it, 1, -1);
		for (it = temp.begin();it != temp.end();++it) {
			ans.insert(*it);
		}
		for (it = ans.begin();it != ans.end();++it) {
			printf("%d\n", *it);
		}
	}
	else {
		printf("Error: %d components", Block);
	}
	return 0;
}

PAT A1034 Head of a Gang

难题,copy了两边还不太会写,这题中有很多细节,如对图中环的处理,为了边权不被漏加需要先累加边权,再去考虑递归访问,而这样会导致重复计算,因此累加了边之后将边删除

#include<iostream>
#include<string>
#include<map>
using namespace std;
const int maxn = 2010;

map<int, string> intToString;
map<string, int> stringToInt;
map<string, int> Gang;
int G[maxn][maxn] = { 0 }, weight[maxn] = { 0 };//边权和点权
bool vis[maxn] = { false };
int n, k, numPerson = 0;

void DFS(int nowVisit, int& head, int& numMember, int& sumValue) {
	vis[nowVisit] = true;
	++numMember;
	if (weight[nowVisit] > weight[head]) head = nowVisit;
	for (int i = 0;i < numPerson;++i) {
		if (G[nowVisit][i] > 0) {//先累加边权
			sumValue += G[nowVisit][i];
			G[nowVisit][i] = G[i][nowVisit] = 0;//可能导致边权重复计算,故需要在累加边权后删除此边
			if (vis[i] == false) {//后考虑结点递归访问的问题
				DFS(i, head, numMember, sumValue);
			}
		}
	}

}

void DFSTrave() {
	for (int i = 0;i < numPerson;++i) {
		if (vis[i] == false) {
			int head = i, numMember = 0, sumValue = 0;
			DFS(i, head, numMember, sumValue);
			if (numMember > 2 && sumValue > k) {
				Gang[intToString[head]] = numMember;
			}
		}
	}
}

int change(string str) {
	if (stringToInt.find(str) != stringToInt.end()) {
		return stringToInt[str];
	}
	else {
		stringToInt[str] = numPerson;
		intToString[numPerson] = str;
		return numPerson++;
	}
}

int main() {
	cin >> n >> k;
	for (int i = 0;i < n;++i) {
		string str1, str2;
		int w;
		cin >> str1 >> str2 >> w;
		int id1 = change(str1);
		int id2 = change(str2);
		weight[id1] += w;
		weight[id2] += w;
		G[id1][id2] += w;
		G[id2][id1] += w;
	}
	DFSTrave();
	cout << Gang.size() << endl;
	for (map<string, int>::iterator it = Gang.begin();it != Gang.end();++it) {
		cout << it->first << " " << it->second << endl;
	}
	return 0;
}

PAT A1076 Forwards on Weibo

改错半小时——时刻注意数组从0开始还是从1开始!!!
邻接矩阵写法

#include<cstdio>
#include<queue>
using namespace std;
const int maxn = 1010;
struct node {
	int v;//编号
	int layer;//层号 
}Node[maxn];
int n, l;
bool G[maxn][maxn] = { false }, inq[maxn] = { false };//单向图G两点间有无通路,inq是否进过队列 

int BFS(int u) {
	int cnt = 0;
	Node[u].layer = 0;
	queue<node> qu;
	qu.push(Node[u]);
	inq[u] = true;
	while (!qu.empty()) {
		node top = qu.front();
		qu.pop();
		for (int v = 1;v <= n;++v) {
			if (inq[v] == false && G[top.v][v] == true) {
				Node[v].layer = top.layer + 1;
				if (Node[v].layer <= l) {
					qu.push(Node[v]);
					inq[v] = true;
					++cnt;
				}
				else break;
			}
		}
	}
	for (int i = 0;i < maxn;++i) inq[i] = false;
	return cnt;
}

int main() {
	int w, k;
	scanf("%d %d", &n, &l);
	for (int i = 1;i <= n;++i) {
		Node[i].v = i;
		scanf("%d", &w);
		for (int j = 0;j < w;++j) {
			int temp;
			scanf("%d", &temp);
			G[temp][i] = true;//temp关注了i 
		}
	}
	scanf("%d", &k);
	for (int i = 0;i < k;++i) {
		int u;
		scanf("%d", &u);
		printf("%d\n", BFS(u));
	}


	return 0;
}

《算法笔记》10.4小节——图算法专题->最短路径

Codeup

算法7-15:迪杰斯特拉最短路径算法

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 55;
const int INF = 1 << 30 - 1;
int n, st;
int dis[maxn][maxn] = { 0 }, d[maxn];
bool vis[maxn] = { false };

void Dijkstra(int s) {
	fill(d, d + maxn, INF);
	d[s] = 0;
	for (int i = 0;i < n;++i) {//循环n次 
		int u = -1, MIN = INF;
		for (int j = 0;j < n;++j) {//找到最近结点 
			if (vis[j] == false && d[j] < MIN) {
				u = j;
				MIN = d[j];
			}
		}
		if (u == -1) return;
		vis[u] = true;
		for (int v = 0;v < n;++v) {
			if (dis[u][v] != INF && vis[v] == false && d[u] + dis[u][v] < d[v]) {
				d[v] = d[u] + dis[u][v];
			}
		}
	}
}

int main() {
	while (scanf("%d %d", &n, &st) != EOF) {
		for (int i = 0;i < n;++i) {
			for (int j = 0;j < n;++j) {
				scanf("%d", &dis[i][j]);
				if (dis[i][j] == 0) dis[i][j] = INF;
			}
		}
		Dijkstra(st);
		for (int i = 0;i < n;++i) {
			if (i != st) {
				if (d[i] != INF) printf("%d ", d[i]);
				else printf("-1 ");
			}

		}
		printf("\n");
	}
	return 0;
}

算法7-16:弗洛伊德最短路径算法

还是Floyd算法好写

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 55;
const int INF = 1 << 30 - 1;
int dis[maxn][maxn];
int n;

void Floyd() {
	for (int k = 0;k < n;++k) {
		for (int j = 0;j < n;++j) {
			for (int i = 0;i < n;++i) {
				if (dis[i][k] + dis[k][j] < dis[i][j]) dis[i][j] = dis[i][k] + dis[k][j];
			}
		}
	}
}

int main() {
	while (scanf("%d", &n) != EOF) {
		for (int i = 0;i < n;++i) {
			for (int j = 0;j < n;++j) {
				scanf("%d", &dis[i][j]);
				if (i != j && dis[i][j] == 0) dis[i][j] = INF;
			}
		}
		Floyd();
		for (int i = 0;i < n;++i) {
			for (int j = 0;j < n;++j) {
				if (dis[i][j] != INF) printf("%d ", dis[i][j]);
				else printf("-1 ");
			}
			printf("\n");
		}
	}

	return 0;
}

最短路径

提交了三十次发现错误在fill(vis,vis+maxn,false);,原因是Codeup会输入很多组数据,如果不在每一组中都清空一次,就会导致错误答案,所以一定要注意!!!

题目中M(M<=500),因此最大会有2^ 500,这个数很大,不能直接表示出来,因此用到了题目中的最后一句话,“数值太大的以MOD 100000 的结果输出”,因此在求2的多少次方的时候要及时取余,但是这也带来了一个问题,取余之后就不是原先的数据了,大小也不准确了,怎么再求最短路径。题目中还有一句话很重要:“第K条道路(K从0开始)的长度为2^K”,因此两个点之间,最早出现的道路一定就是这两个点之间的最短路径。(因为1+2 ^ 1+2 ^ 2+2 ^(k-1)=2 ^k-1<2 ^k就算前面所有的点都是路径上的点,它们加起来也没有最后一条路径的长度长),因此只要前面的点之间是连通的,这些路径就是最短路径,后面再出现的路径直接跳过,不接收。
来自问题 C: 最短路径

来自大佬的解释,期间我想数据溢出后不接收,但这是不行的,因为如果这路是两点间唯一通路则必须采纳。所以还是得用并查集

#include<cstdio>
#include<algorithm>
using namespace std; 
const int maxn=110;
const int INF=1<<30-1;
const int MOD=100000;
int n,m;
int dis[maxn][maxn];
int d[maxn],father[maxn];
bool vis[maxn]={false};

int f(int p){
	int res=1;
	for(int i=0;i<p;++i){
		res=(res*2)%MOD;
	}
	return res;
}

void Dijkstra(int s){
	fill(vis,vis+maxn,false);//无语的一句
	fill(d,d+maxn,INF);
	d[s]=0;
	for(int i=0;i<n;++i){
		int u=-1,MIN=INF;
		for(int j=0;j<n;++j){
			if(d[j]<MIN&&vis[j]==false){
				u=j;
				MIN=d[j];
			}
		}
		if(u==-1) return;
		vis[u]=true;
		for(int v=0;v<n;++v){
			if(dis[u][v]!=INF&&d[u]+dis[u][v]<d[v]&&vis[v]==false){
				d[v]=d[u]+dis[u][v];
			}
		}
	}
}

int findFather(int x){
	int a=x;
	while(x!=father[x]) x=father[x];
	while(a!=father[a]){
		int z=a;
		a=father[a];
		father[z]=x;
	}
	return x;
}

void Union(int a,int b,int i){
	int faA=findFather(a);
	int faB=findFather(b);
	if(faA!=faB){
		father[faA]=faB;
		dis[a][b]=dis[b][a]=f(i);
	}
}

int main(){
	while(scanf("%d %d",&n,&m)!=EOF){
		fill(dis[0],dis[0]+maxn*maxn,INF);
		for(int i=0;i<n;++i) father[i]=i;
		for(int i=0;i<m;++i){
			int u,v;
			scanf("%d %d",&u,&v);
			Union(u,v,i);
		}
		Dijkstra(0);
		for(int i=1;i<n;++i){
			if(d[i]!=INF) printf("%d\n",d[i]%MOD);
			else printf("-1\n");
		}
	}
	return 0;
}

最短路径

SPFA写法

写了一点的时候感觉用Dijkstra+DFS可能要好一点,但还是把它给写完了。

#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn = 1010;
const int INF = 1 << 30 - 1;
struct node {
	int v;
	int dis;
	node(int _v, int _dis) :v(_v), dis(_dis) {}
};
int n, m, st, ed;
int d[maxn], pre[maxn];
bool inq[maxn];
vector<node> Adj[maxn];

int cal(int x) {//计算字典序 
	int count = 0;
	while (x != st) {
		count += x;
		x = pre[x];
	}
	return count;//可以不用管st的值,因为所有人都有st 
}

void SPFA(int s) {
	memset(inq, false, sizeof(inq));
	for (int i = 0;i < n;++i) pre[i] = i;
	fill(d, d + maxn, INF);
	queue<int> qu;
	qu.push(s);
	inq[s] = true;
	d[s] = 0;
	while (!qu.empty()) {
		int u = qu.front();
		qu.pop();
		inq[u] = false;
		for (int j = 0;j < Adj[u].size();++j) {
			int v = Adj[u][j].v;
			int dis = Adj[u][j].dis;
			if (d[u] + dis < d[v]) {
				d[v] = d[u] + dis;
				pre[v] = u;
				if (!inq[v]) {
					inq[v] = true;
					qu.push(v);
				}
			}
			else if (d[u] + dis == d[v]) {
				if (cal(v) > cal(u) + v) {
					pre[v] = u;
					if (!inq[v]) {
						inq[v] = true;
						qu.push(v);
					}
				}
			}
		}
	}
}

void printPath(int v) {
	if (v == st) {
		printf("%d", v);
		return;
	}
	printPath(pre[v]);
	printf(" %d", v);
}

int main() {
	while (scanf("%d %d %d %d", &n, &m, &st, &ed) != EOF) {
		for (int i = 0;i < m;++i) {
			int u, v, l;
			scanf("%d %d %d", &u, &v, &l);
			Adj[u].push_back(node(v, l));
			Adj[v].push_back(node(u, l));
		}
		SPFA(st);
		if (d[ed] != INF) {
			printf("%d\n", d[ed]);
			printPath(ed);
			printf("\n");
		}
		else {
			printf("can't arrive\n");
		}
		for(int i=0;i<maxn;++i){//本例完擦除Adj 
			if(Adj[i].size()>0){
				Adj[i].clear();
			}
		}
	}


	return 0;
}
Dijkstra+DFS

错误——邻接表AC了,用下面的邻接表改得邻接矩阵,答案错误,原因未知,欢迎大家探讨。

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1010;
const int INF = 1 << 30 - 1;
int n, m, st, ed;
vector<int> pre[maxn];
vector<int> path, tempPath;
int d[maxn], G[maxn][maxn];
bool vis[maxn];

void Dijkstra(int s) {
	memset(vis, false, sizeof(vis));
	fill(d, d + maxn, INF);
	d[s] = 0;
	for (int i = 0;i < n;++i) {
		int u = -1, MIN = INF;
		for (int j = 1;j <= n;++j) {
			if (vis[j] == false && d[j] < MIN) {
				u = j;
				MIN = d[j];
			}
		}

		if (u == -1) return;
		vis[u] = true;
		for (int v = 1;v <= n;++v) {
			if (vis[v] == false && G[u][v] != INF) {
				if (d[u] + G[u][v] < d[v]) {
					d[v] = d[u] + G[u][v];
					pre[v].clear();
					pre[v].push_back(u);
				}
				else if (d[u] + G[u][v] == d[v]) {
					pre[v].push_back(u);
				}
			}
		}
	}
}

int optvalue = INF;
void DFS(int v) {
	if (v == st) {
		tempPath.push_back(v);
		int value = 0;
		for (int i = tempPath.size() - 1;i >= 0;--i) {
			value += tempPath[i];
		}
		if (value < optvalue) {
			optvalue = value;
			path = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	tempPath.push_back(v);
	for (int i = 0;i < pre[v].size();++i) {
		DFS(pre[v][i]);
	}
	tempPath.pop_back();
}

int main() {
	while (scanf("%d %d %d %d", &n, &m, &st, &ed) != EOF) {
		fill(G[0], G[0] + maxn * maxn, INF);
		for (int i = 0;i < maxn;++i) G[i][i] = 0;
		path.clear(), tempPath.clear();
		optvalue = INF;
		for (int i = 0;i < maxn;++i) {
			if (pre[i].size() > 0) pre[i].clear();
		}
		for (int i = 0;i < m;++i) {
			int u, v, l;
			scanf("%d %d %d", &u, &v, &l);
			G[u][v] = G[v][u] = l;
		}
		Dijkstra(st);
		DFS(ed);
		if (d[ed] != INF) {
			printf("%d\n", d[ed]);
			for (int i = path.size() - 1;i >= 0;--i) {
				if (i < path.size() - 1) printf(" ");
				printf("%d", path[i]);
			}
			printf("\n");
		}
		else printf("can't arrive\n");
	}
	return 0;
}

邻接表

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1010;
const int INF = 1 << 30 - 1;
struct node {
	int v, dis;
	node(int _v, int _dis) : v(_v), dis(_dis) {}
};
int n, m, st, ed;
vector<node> Adj[maxn];
vector<int> pre[maxn];
vector<int> path, tempPath;
int d[maxn];
bool vis[maxn];

void Dijkstra(int s) {
	memset(vis, false, sizeof(vis));
	fill(d, d + maxn, INF);
	d[s] = 0;
	for (int i = 0;i < n;++i) {
		int u = -1, MIN = INF;
		for (int j = 1;j <= n;++j) {
			if (vis[j] == false && d[j] < MIN) {
				u = j;
				MIN = d[j];
			}
		}

		if (u == -1) return;
		vis[u] = true;
		for (int j = 0;j < Adj[u].size();++j) {
			int v = Adj[u][j].v;
			int dis = Adj[u][j].dis;
			if (vis[v] == false && dis != INF) {
				if (d[u] + dis < d[v]) {
					d[v] = d[u] + dis;
					pre[v].clear();
					pre[v].push_back(u);
				}
				else if (d[u] + dis == d[v]) {
					pre[v].push_back(u);
				}
			}
		}
	}
}

int optvalue = INF;
void DFS(int v) {
	if (v == st) {
		tempPath.push_back(v);
		int value = 0;
		for (int i = tempPath.size() - 1;i >= 0;--i) {
			value += tempPath[i];
		}
		if (value < optvalue) {
			optvalue = value;
			path = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	tempPath.push_back(v);
	for (int i = 0;i < pre[v].size();++i) {
		DFS(pre[v][i]);
	}
	tempPath.pop_back();
}

int main() {
	while (scanf("%d %d %d %d", &n, &m, &st, &ed) != EOF) {
		path.clear(), tempPath.clear();
		optvalue = INF;
		for (int i = 0;i < maxn;++i) {
			if (pre[i].size() > 0) pre[i].clear();
			if (Adj[i].size() > 0) Adj[i].clear();
		}
		for (int i = 0;i < m;++i) {
			int u, v, l;
			scanf("%d %d %d", &u, &v, &l);
			Adj[u].push_back(node(v, l));
			Adj[v].push_back(node(u, l));
		}
		Dijkstra(st);
		DFS(ed);
		if (d[ed] != INF) {
			printf("%d\n", d[ed]);
			for (int i = path.size() - 1;i >= 0;--i) {
				if (i < path.size() - 1) printf(" ");
				printf("%d", path[i]);
			}
			printf("\n");
		}
		else printf("can't arrive\n");
	}
	return 0;
}

最短路径问题

Dijkstra

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1010;
const int INF = 1 << 30 - 1;
int G[maxn][maxn], d[maxn];
int cost[maxn][maxn], c[maxn];
bool vis[maxn];
int n, m, st, ed;

void dijkstra(int s) {
	memset(vis, false, sizeof(vis));
	fill(d, d + maxn, INF);
	fill(c, c + maxn, INF);
	d[s] = 0;
	c[s] = 0;
	for (int i = 0;i < n;++i) {
		int u = -1, MIN = INF;
		for (int j = 1;j <= n;++j) {
			if (vis[j] == false && d[j] < MIN) {
				u = j;
				MIN = d[j];
			}
		}
		if (u == -1) return;
		vis[u] = true;
		for (int v = 1;v <= n;++v) {
			if (vis[v] == false && G[u][v] != INF) {
				if (d[u] + G[u][v] < d[v]) {
					d[v] = d[u] + G[u][v];
					c[v] = c[u] + cost[u][v];
				}
				else if (d[u] + G[u][v] == d[v]) {
					if (c[u] + cost[u][v] < c[v]) {
						c[v] = c[u] + cost[u][v];
					}
				}
			}
		}
	}
}

int main() {
	while (scanf("%d %d", &n, &m), n || m) {
		fill(G[0], G[0] + maxn * maxn, INF);
		fill(cost[0], cost[0] + maxn * maxn, INF);
		for (int i = 0;i < m;++i) {
			int a, b, d, p;
			scanf("%d %d %d %d", &a, &b, &d, &p);
			G[a][b] = G[b][a] = d;
			cost[a][b] = cost[b][a] = p;
		}
		scanf("%d %d", &st, &ed);
		dijkstra(st);
		printf("%d %d\n", d[ed], c[ed]);

	}
	return 0;
}

配套实战演练

PAT A1003 Emergency

Dijkstra
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXV = 510, INF = 1 << 30;
bool vis[MAXV] = { false };
int w[MAXV] = { 0 }, num[MAXV] = { 0 }, weight[MAXV];
int G[MAXV][MAXV] = { 0 }, d[MAXV];
int n, m;
void dijkstra(int s) {
	fill(d, d + MAXV, INF);
	d[s] = 0;
	num[s] = 1;
	w[s] = weight[s];
	for (int i = 0;i < n;++i) {
		int u = -1, MIN = INF;
		for (int j = 0;j < n;++j) {
			if (vis[j] == false && d[j] < MIN) {//找出下一个点 
				u = j;
				MIN = d[j];
			}
		}
		if (u == -1) return;
		vis[u] = true;
		for (int v = 0;v < n;++v) {
			if (G[u][v] != 0 && vis[v] == false) {
				if (d[u] + G[u][v] < d[v]) {
					d[v] = d[u] + G[u][v];
					w[v] = w[u] + weight[v];
					num[v] = num[u];
				}
				else if (d[u] + G[u][v] == d[v]) {
					num[v] += num[u];
					if (w[u] + weight[v] > w[v]) {
						w[v] = w[u] + weight[v];
					}
				}
			}
		}
	}
}

int main() {
	int 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 s, d, l;
		scanf("%d %d %d", &s, &d, &l);
		G[s][d] = G[d][s] = l;
	}
	dijkstra(c1);
	printf("%d %d", num[c2], w[c2]);
	return 0;
}
Dijkstra+DFS
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 510;
const int INF = 1 << 30 - 1;
int n, m, st, ed;
int weight[maxn];
int G[maxn][maxn], d[maxn];
bool vis[maxn] = { false };
vector<int> pre[maxn];

void dijkstra(int s) {
	fill(d, d + maxn, INF);
	d[s] = 0;
	for (int i = 0;i < n;++i) {
		int u = -1, MIN = INF;
		for (int j = 0;j < n;++j) {
			if (vis[j] == false && d[j] < MIN) {
				u = j;
				MIN = d[j];
			}
		}
		if (u == -1) return;
		vis[u] = true;
		for (int v = 0;v < n;++v) {
			if (vis[v] == false && G[u][v] != INF) {
				if (d[u] + G[u][v] < d[v]) {
					d[v] = d[u] + G[u][v];
					pre[v].clear();
					pre[v].push_back(u);
				}
				else if (d[u] + G[u][v] == d[v]) {
					pre[v].push_back(u);
				}
			}
		}
	}
}

vector<int> path, tempPath;
int optvalue = 0, num = 0;
void DFS(int v) {
	if (v == st) {
		tempPath.push_back(v);
		++num;
		int value = 0;
		for (int i = tempPath.size() - 1;i >= 0;--i) {
			value += weight[tempPath[i]];//这里不要写成weight[i]了 
		}
		if (value > optvalue) {
			optvalue = value;
			path = tempPath;
		}
		tempPath.pop_back();
	}
	tempPath.push_back(v);
	for (int i = 0;i < pre[v].size();++i) {
		DFS(pre[v][i]);
	}
	tempPath.pop_back();
}

int main() {
	scanf("%d %d %d %d", &n, &m, &st, &ed);
	fill(G[0], G[0] + maxn * maxn, INF);
	for (int i = 0;i < n;++i) {
		scanf("%d", &weight[i]);
	}
	for (int i = 0;i < m;++i) {
		int u, v, l;
		scanf("%d %d %d", &u, &v, &l);
		G[u][v] = G[v][u] = l;
	}
	dijkstra(st);
	DFS(ed);
	printf("%d %d", num, optvalue);
	return 0;
}
Bellman-Ford

要多写几遍,熟能生巧

#include<cstdio>
#include<set>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 510;
const int INF = 1 << 30 - 1;
struct node {
	int v;//边的临界点
	int dis;//边权 
	node(int _v, int _dis) : v(_v), dis(_dis) {}
};

int n, m, st, ed;
int weight[maxn], w[maxn] = { 0 };
int d[maxn], num[maxn] = { 0 };
vector<node> Adj[maxn];
set<int> pre[maxn];//使用set可以防止前置结点重复, 

void Bellman(int s) {
	fill(d, d + maxn, INF);
	w[s] = weight[s];
	d[s] = 0;
	num[s] = 1;
	for (int i = 0;i < n - 1;++i) {//执行n-1轮操作 
		for (int u = 0;u < n;++u) {//每轮操作遍历所有边 
			for (int j = 0;j < Adj[u].size();++j) {
				int v = Adj[u][j].v;
				int dis = Adj[u][j].dis;
				if (d[u] + dis < d[v]) {
					d[v] = d[u] + dis;
					w[v] = w[u] + weight[v];
					pre[v].clear();//清空所有前置结点 
					pre[v].insert(u);//将u加入 
					num[v] = num[u];
				}
				else if (d[u] + dis == d[v]) {
					if (w[v] < w[u] + weight[v]) {
						w[v] = w[u] + weight[v];
					}
					pre[v].insert(u);//第二条最短路径 
					num[v] = 0;
					set<int>::iterator it;
					for (it = pre[v].begin();it != pre[v].end();++it) {//num[v]的值等于v的所有前置结点值的和 
						num[v] += num[*it];
					}
				}
			}
		}
	}

}

int main() {
	scanf("%d %d %d %d", &n, &m, &st, &ed);
	for (int i = 0;i < n;++i) scanf("%d", &weight[i]);
	for (int i = 0;i < m;++i) {
		int u, v, l;
		scanf("%d %d %d", &u, &v, &l);
		Adj[u].push_back(node(v, l));
		Adj[v].push_back(node(u, l));
	}
	Bellman(st);
	printf("%d %d", num[ed], w[ed]);
	return 0;
}
SPFA
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<set>
using namespace std;
const int maxn = 510;
const int INF = 1 << 30 - 1;

struct node {
	int v;
	int dis;
	node(int _v, int _dis) : v(_v), dis(_dis) {}
};

int n, m, st, ed;
int weight[maxn], w[maxn] = { 0 };
int d[maxn], num[maxn] = { 0 };
vector<node> Adj[maxn];
bool inq[maxn] = { false };
set<int> pre[maxn];

void SPFA(int s) {
	fill(d, d + maxn, INF);
	d[s] = 0;
	w[s] = weight[s];
	num[s] = 1;
	inq[s] = true;
	queue<int> qu;//这里是int类型的
	qu.push(s);
	while (!qu.empty()) {
		int u = qu.front();
		qu.pop();
		inq[u] = false;
		for (int j = 0;j < Adj[u].size();++j) {
			int v = Adj[u][j].v;
			int dis = Adj[u][j].dis;
			if (d[u] + dis < d[v]) {
				d[v] = d[u] + dis;
				w[v] = w[u] + weight[v];
				num[v] = num[u];
				pre[v].clear();
				pre[v].insert(u);
				if (inq[v] == false) {
					qu.push(v);
					inq[v] = true;
				}
			}
			else if (d[u] + dis == d[v]) {
				if (w[u] + weight[v] > w[v]) {
					w[v] = w[u] + weight[v];
				}
				pre[v].insert(u);
				num[v] = 0;
				set<int>::iterator it;
				for (it = pre[v].begin();it != pre[v].end();++it) {
					num[v] += num[*it];
				}
				if (inq[v] == false) {//这种情况同样需要入栈,原因是可能导致其他结点中有更小的第二或第三标尺(即weight和num) 
					qu.push(v);
					inq[v] = true;
				}
			}
		}
	}
}

int main() {
	scanf("%d %d %d %d", &n, &m, &st, &ed);
	for (int i = 0;i < n;++i) scanf("%d", &weight[i]);
	for (int i = 0;i < m;++i) {
		int u, v, l;
		scanf("%d %d %d", &u, &v, &l);
		Adj[u].push_back(node(v, l));
		Adj[v].push_back(node(u, l));
	}
	SPFA(st);
	printf("%d %d\n", num[ed], w[ed]);
}

PAT A1018 Public Bike Management

这题最大的坑点在于没有告诉从PBMC出发到问题站点的过程中就要把所有的站点调整到最有状态,即必须从头开始计算remain和need,for (int i = tempPath.size() - 2;i >= 0;--i)体现在这一句,否则将会有错点。

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 510;
const int INF = 1 << 30 - 1;
int G[maxn][maxn], d[maxn];
int weight[maxn];
int cmax, n, sp, m;//每个结点最大容量,结点总量,问题结点编号,路数;
bool vis[maxn] = { false };
vector<int> pre[maxn];
vector<int> path, tempPath;

void Dijkstra(int s) {
	fill(d, d + maxn, INF);
	d[s] = 0;
	for (int i = 0;i <= n;++i) {
		int u = -1, MIN = INF;
		for (int j = 0;j <= n;++j) {
			if (vis[j] == false && d[j] < MIN) {
				u = j;
				MIN = d[j];
			}
		}
		if (u == -1) return;
		vis[u] = true;
		for (int v = 0;v <= n;++v) {
			if (vis[v] == false && G[u][v] != INF) {
				if (d[u] + G[u][v] < d[v]) {
					d[v] = d[u] + G[u][v];
					pre[v].clear();
					pre[v].push_back(u);
				}
				else if (d[u] + G[u][v] == d[v]) {
					pre[v].push_back(u);
				}
			}
		}
	}
}

int minNeed = INF, minRemain = INF;
void DFS(int v) {
	if (v == 0) {
		tempPath.push_back(v);
		//需要携带和带回的数目 
		int need = 0, remain = 0;
		for (int i = tempPath.size() - 2;i >= 0;--i) {//此处必须倒着枚举, 
			int id = tempPath[i];
			if (weight[id] > cmax / 2) {//需要带走一部分车 
				remain += (weight[id] - cmax / 2);
			}
			else {
				if (remain > cmax / 2 - weight[id]) {//足够补给 
					remain -= cmax / 2 - weight[id];
				}
				else {//不够补给 
					need += cmax / 2 - weight[id] - remain;
					remain = 0;
				}
			}
		}
		if (need < minNeed) {//需要从PBMC携带的自行车数目更少 
			minNeed = need;
			minRemain = remain;
			path = tempPath;
		}
		else if (need == minNeed && remain < minRemain) {//需要带回的更少 
			minRemain = remain;
			path = tempPath;
		}
		tempPath.pop_back();
		return;//别忘了 
	}
	tempPath.push_back(v);
	for (int i = 0;i < pre[v].size();++i) {
		DFS(pre[v][i]);
	}
	tempPath.pop_back();
}

int main() {
	scanf("%d %d %d %d", &cmax, &n, &sp, &m);
	fill(G[0], G[0] + maxn * maxn, INF);
	for (int i = 1;i <= n;++i) {
		scanf("%d", &weight[i]);//点权 
	}
	for (int i = 0;i < m;++i) {
		int u, v, l;
		scanf("%d %d %d", &u, &v, &l);
		G[u][v] = G[v][u] = l;//边权 
	}
	Dijkstra(0);
	DFS(sp);
	printf("%d ", minNeed);
	for (int i = path.size() - 1;i >= 0;--i) {
		if (i < path.size() - 1) printf("->");
		printf("%d", path[i]);
	}
	printf(" %d", minRemain);
	return 0;
}

PAT A1030 Travel Plan

Dijkstra算法
#include<cstdio>
#include<algorithm>
using namespace std;

const int maxn = 510;
const int INF = 1 << 30 - 1;
int n, m, st, ed;
int G[maxn][maxn] = { 0 }, dis[maxn];
int cost[maxn][maxn] = { 0 }, c[maxn];
int pre[maxn];
bool vis[maxn] = { false };

void dijkstra(int s) {
	fill(dis, dis + maxn, INF);
	fill(c, c + maxn, INF);
	dis[s] = 0;
	c[s] = 0;
	for (int i = 0;i < n;++i) pre[i] = i;
	for (int i = 0;i < n;++i) {
		int u = -1, MIN = INF;
		for (int j = 0;j < n;++j) {
			if (vis[j] == false && dis[j] < MIN) {
				u = j;
				MIN = dis[j];
			}
		}
		if (u == -1) return;
		vis[u] = true;
		for (int v = 0;v < n;++v) {
			if (G[u][v] != INF && vis[v] == false) {
				if (dis[u] + G[u][v] < dis[v]) {
					dis[v] = dis[u] + G[u][v];
					c[v] = c[u] + cost[u][v];
					pre[v] = u;
				}
				else if (dis[u] + G[u][v] == dis[v] && c[u] + cost[u][v] < c[v]) {
					c[v] = c[u] + cost[u][v];
					pre[v] = u;
				}
			}
		}
	}

}

void printpath(int s, int d) {
	if (s == d) {
		printf("%d ", s);
		return;
	}
	printpath(s, pre[d]);
	printf("%d ", d);
}

int main() {
	scanf("%d %d %d %d", &n, &m, &st, &ed);
	fill(G[0], G[0] + maxn * maxn, INF);//记得初始化图 
	for (int i = 0;i < m;++i) {
		int u, v, D, C;
		scanf("%d %d %d %d", &u, &v, &D, &C);
		G[u][v] = G[v][u] = D;
		cost[u][v] = cost[v][u] = C;
	}
	dijkstra(st);
	printpath(st, ed);
	printf("%d %d", dis[ed], c[ed]);
	return 0;
}
Dijkstra+DFS
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;

const int maxn = 510;
const int INF = 1 << 30 - 1;
int n, m, st, ed;
int G[maxn][maxn] = { 0 }, d[maxn];
int cost[maxn][maxn] = { 0 };
vector<int> pre[maxn];
bool vis[maxn] = { false };

void Dijkstra(int s) {
	fill(d, d + maxn, INF);
	d[s] = 0;
	for (int i = 0;i < n;++i) {
		int u = -1, MIN = INF;
		for (int j = 0;j < n;++j) {
			if (vis[j] == false && d[j] < MIN) {
				u = j;
				MIN = d[j];
			}
		}
		if (u == -1) return;
		vis[u] = true;
		for (int v = 0;v < n;++v) {
			if (vis[v] == false && G[u][v] != INF) {
				if (d[u] + G[u][v] < d[v]) {
					d[v] = d[u] + G[u][v];
					pre[v].clear();
					pre[v].push_back(u);
				}
				else if (d[u] + G[u][v] == d[v]) {
					pre[v].push_back(u);
				}
			}
		}
	}
}

int optvalue = INF;
vector<int> path, tempPath;
void DFS(int s, int v) {
	if (v == s) {
		tempPath.push_back(v);
		int value = 0;
		for (int i = tempPath.size() - 1;i > 0;--i) {
			int id = tempPath[i], idNext = tempPath[i - 1];
			value += cost[id][idNext];
		}
		if (value < optvalue) {
			optvalue = value;
			path = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	tempPath.push_back(v);
	for (int i = 0;i < pre[v].size();++i) {
		DFS(s, pre[v][i]);
	}
	tempPath.pop_back();
}

int main() {
	scanf("%d %d %d %d", &n, &m, &st, &ed);
	fill(G[0], G[0] + maxn * maxn, INF);//记得初始化图 
	for (int i = 0;i < m;++i) {
		int u, v, D, C;
		scanf("%d %d %d %d", &u, &v, &D, &C);
		G[u][v] = G[v][u] = D;
		cost[u][v] = cost[v][u] = C;
	}
	Dijkstra(st);
	DFS(st, ed);
	for (int i = path.size() - 1;i >= 0;--i) printf("%d ", path[i]);
	printf("%d %d", d[ed], optvalue);
	return 0;
}
SPFA
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 510;
const int INF = 1 << 30 - 1;
struct node {
	int v;
	int dis, cost;
	node(int _v, int _dis, int _cost) :v(_v), dis(_dis), cost(_cost) {}
};
int n, m, st, ed;
vector<node> Adj[maxn];
int d[maxn], c[maxn], pre[maxn];
bool inq[maxn] = { false };

void SPFA(int s) {
	fill(d, d + maxn, INF);
	fill(c, c + maxn, INF);
	for (int i = 0;i < n;++i) pre[i] = i;
	queue<int> qu;
	d[s] = 0;
	c[s] = 0;
	qu.push(s);
	inq[s] = true;
	while (!qu.empty()) {
		int u = qu.front();
		inq[u] = false;
		qu.pop();
		for (int j = 0;j < Adj[u].size();++j) {//这里j<Adj[u].size 
			int v = Adj[u][j].v;
			int dis = Adj[u][j].dis;
			int cost = Adj[u][j].cost;
			if (d[u] + dis < d[v]) {
				d[v] = d[u] + dis;
				c[v] = c[u] + cost;
				pre[v] = u;
				if (inq[v] == false) {
					qu.push(v);
					inq[v] = true;
				}
			}
			else if (d[u] + dis == d[v] && c[u] + cost < c[v]) {
				c[v] = c[u] + cost;
				pre[v] = u;
				if (inq[v] == false) {
					qu.push(v);
					inq[v] = true;
				}
			}
		}
	}
}

void printPath(int v) {
	if (v == st) {
		printf("%d ", st);
		return;
	}
	printPath(pre[v]);
	printf("%d ", v);
}

int main() {
	scanf("%d %d %d %d", &n, &m, &st, &ed);
	for (int i = 0;i < m;++i) {
		int u, v, ds, cs;
		scanf("%d %d %d %d", &u, &v, &ds, &cs);
		Adj[u].push_back(node(v, ds, cs));
		Adj[v].push_back(node(u, ds, cs));
	}
	SPFA(st);
	printPath(ed);
	printf("%d %d", d[ed], c[ed]);
	return 0;
}

PAT A1072 Gas Station

这题思路不难,但写全对不简单
1、首先是解决顶点编号的问题,我自己的思路,最开始想的是1000以后的用来放加油站,想了下那样每次Dijkstra算法的时候都要跑到1000多,时间复杂度太大,所以就将加油站放在n后面,这同时导致了maxn至少要到1011,这里我将加油站顶点转换为数字的方式是sscanf,有时候这个真的很好用!!!
2、这题题干部分的the minimum distance between the station and any of the residential housing is as far away as possible一定要注意,最开始直接跳过了这句话以为第一评判标准是最小平均值。
3、因为加油站也是实际存在的结点因此Dijkstra算法中遍历顶点的范围为1~(n+m)这点也要注意,同时因为算法要重复多次,因此每一次重复都要重置vis和d(这点在codeup中已经坑了我很长时间了,因此牢记于心)。
4、最后是输出部分,我测试的时候样例数据算出来的平均值是3.25,直接用%1.f输出得到的是3.2,因此我用了round函数四舍五入(PS:看我注释掉的那句测试语句,有时候找不到错误不要盲目的从头到尾检查一遍代码,可以在中间加上一些例如printf(“1”);之类的语句,看看究竟是哪个版块出了问题)
另外写了这么多感觉也没人看。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1050;//1~n存放居民楼,n+1~n+m存放加油站 
const int INF = 1 << 30 - 1;
int n, m, k, ds;
int G[maxn][maxn], d[maxn];
bool vis[maxn];

void Dijkstra(int s) {
	fill(d, d + maxn, INF);
	memset(vis, false, sizeof(vis));
	d[s] = 0;
	for (int i = 1;i <= n + m;++i) {
		int u = -1, MIN = INF;
		for (int j = 1;j <= n + m;++j) {
			if (vis[j] == false && d[j] < MIN) {
				u = j;
				MIN = d[j];
			}
		}
		if (u == -1) return;
		vis[u] = true;
		for (int v = 1;v <= n + m;++v) {
			if (vis[v] == false && G[u][v] != INF && d[u] + G[u][v] < d[v]) {
				d[v] = d[u] + G[u][v];
			}
		}
	}
}

int get_ID(char str[]) {//用黑科技sscanf 
	int res;
	if (str[0] == 'G') {
		str[0] = '+';
		sscanf(str, "%d", &res);
		return res + n;
	}
	else {
		sscanf(str, "%d", &res);
		return res;
	}
}

int main() {
	scanf("%d %d %d %d", &n, &m, &k, &ds);
	fill(G[0], G[0] + maxn * maxn, INF);
	for (int i = 0;i < k;++i) {
		char str1[6], str2[6];
		int u, v, l;
		scanf("%s %s %d", str1, str2, &l);
		u = get_ID(str1);
		v = get_ID(str2);
		G[u][v] = G[v][u] = l;
	}
	int choice;
	double minDis = -1, minAve = INF;
	for (int i = n + m;i > n;--i) {//反向枚举,可选出最小的点 
		Dijkstra(i);
		double tempMinDis = INF, tempAve, sum = 0;
		bool flag = true;//所有居民都在加油站范围内 
		for (int j = 1;j <= n;++j) {
			if (d[j] > ds) {
				flag = false;
				break;
			}
			if (d[j] < tempMinDis) {
				tempMinDis = d[j];
			}
			sum += d[j];
		}
		if (flag == false) continue;//有居民不在加油站范围内 
		tempAve = 1.0 * sum / n;
		//		printf("%f %f %f\n",tempMinDis,sum,tempAve);//测试用的 
		if (tempMinDis > minDis) {//最近距离最远 
			minDis = tempMinDis;
			minAve = tempAve;
			choice = i;
		}
		else if (tempMinDis == minDis && tempAve < minAve) {
			minAve = tempAve;
			choice = i;
		}
		else if (tempMinDis == minDis && tempAve == minAve) {
			choice = i;//因为是倒着遍历,获得最小结点号 
		}
	}
	if (minDis != -1) {
		printf("G%d\n", choice - n);
		printf("%.1f %.1f", minDis, round(minAve * 10) / 10);//测试了一下结果是3.25,直接输出是3.2而答案为3.3因此要四舍五入 
	}
	else printf("No Solution\n");

	return 0;
}

PAT A1087 All Roads Lead to Rome

关于写错一个字母找了半小时bug这点事。

Dijkstra+DFS
#include<cstdio>
#include<iostream>
#include<vector> 
#include<string>
#include<map>
#include<algorithm>
using namespace std;
const int maxn = 210;
const int INF = 1 << 30 - 1;
map<string, int> stringToInt;
map<int, string> intToString;
int n, m, st, cityNum = 0;
int weight[maxn], G[maxn][maxn];
int d[maxn];
bool vis[maxn] = { false };
vector<int> pre[maxn];
vector<int> path, tempPath;

int change(string str) {
	if (stringToInt.find(str) != stringToInt.end()) {
		return stringToInt[str];
	}
	else {
		stringToInt[str] = cityNum;
		intToString[cityNum] = str;
		return cityNum++;
	}
}

void Dijkstra(int s) {
	fill(d, d + maxn, INF);
	d[s] = 0;
	for (int i = 0;i < n;++i) {
		int u = -1, MIN = INF;
		for (int j = 0;j < n;++j) {
			if (vis[j] == false && d[j] < MIN) {
				u = j;
				MIN = d[j];
			}
		}
		if (u == -1) return;
		vis[u] = true;
		for (int v = 0;v < n;++v) {
			if (vis[v] == false && G[u][v] != INF) {
				if (d[u] + G[u][v] < d[v]) {
					d[v] = d[u] + G[u][v];
					pre[v].clear();
					pre[v].push_back(u);
				}
				else if (d[u] + G[u][v] == d[v]) {
					pre[v].push_back(u);
				}
			}
		}
	}
}

int num = 0, happiness = 0;
double avehappiness;
void DFS(int v) {
	if (v == st) {
		++num;
		tempPath.push_back(v);
		int tempHappiness = 0;
		for (int i = 0;i < tempPath.size();++i) {
			tempHappiness += weight[tempPath[i]];
		}
		double tempAvg = 1.0 * tempHappiness / (tempPath.size() - 1);
		if (tempHappiness > happiness) {
			path = tempPath;
			happiness = tempHappiness;
			avehappiness = tempAvg;
		}
		else if (tempHappiness == happiness && tempAvg > avehappiness) {
			avehappiness = tempAvg;
			path = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	tempPath.push_back(v);
	for (int i = 0;i < pre[v].size();++i) {
		DFS(pre[v][i]);
	}
	tempPath.pop_back();
}

int main() {
	string str;
	cin >> n >> m >> str;
	fill(G[0], G[0] + maxn * maxn, INF);
	st = change(str);
	for (int i = 0;i < n - 1;++i) {
		cin >> str;
		int k = change(str);
		cin >> weight[k];
	}
	for (int i = 0;i < m;++i) {
		string str1, str2;
		int u, v, l;
		cin >> str1 >> str2 >> l;
		u = change(str1);
		v = change(str2);
		G[u][v] = G[v][u] = l;
	}
	Dijkstra(st);
	DFS(stringToInt["ROM"]);
	int cost = 0;
	for (int i = path.size() - 1;i > 0;--i) {
		int u = path[i], v = path[i - 1];
		cost += G[u][v];
	}
	cout << num << " " << cost << " " << happiness << " " << (int)avehappiness << endl;
	for (int i = path.size() - 1;i >= 0;--i) {
		if (i < path.size() - 1) cout << "->";
		cout << intToString[path[i]];
	}
	cout << endl;
	return 0;
}
Dijkstra
#include<cstdio>
#include<iostream>
#include<map>
#include<string>
#include<algorithm>
using namespace std;
const int maxn = 210;
const int INF = 1 << 30 - 1;
int n, m, st, numCity = 0;
int G[maxn][maxn], d[maxn];
int weight[maxn], w[maxn];
int num[maxn] = { 0 }, pt[maxn], pre[maxn];//一堆数组 
bool vis[maxn] = { false };
map<int, string> numToCity;
map<string, int> cityToNum;

void Dijkstra(int s) {
	fill(d, d + maxn, INF);
	fill(w, w + maxn, INF);
	d[s] = 0;
	w[s] = weight[s];
	num[s] = 1;
	pt[s] = 0;
	for (int i = 0;i < n;++i) {
		int u = -1, MIN = INF;
		for (int j = 0;j < n;++j) {
			if (vis[j] == false && d[j] < MIN) {
				u = j;
				MIN = d[j];
			}
		}
		if (u == -1) return;
		vis[u] = true;
		for (int v = 0;v < n;++v) {
			if (vis[v] == false && G[u][v] != INF) {
				if (d[u] + G[u][v] < d[v]) {
					d[v] = d[u] + G[u][v];
					w[v] = w[u] + weight[v];
					num[v] = num[u];
					pt[v] = pt[u] + 1;
					pre[v] = u;
				}
				else if (d[u] + G[u][v] == d[v]) {
					num[v] += num[u];
					if (w[u] + weight[v] > w[v]) {
						pre[v] = u;
						w[v] = w[u] + weight[v];
						pt[v] = pt[u] + 1;
					}
					else if (w[u] + weight[v] == w[v]) {
						double aveu = 1.0 * (w[u] + weight[v]) / (pt[u] + 1);
						double avev = 1.0 * w[v] / pt[v];
						if (aveu > avev) {
							pre[v] = u;
							pt[v] = pt[u] + 1;
						}
					}
				}
			}
		}
	}
}


int change(string str) {
	if (cityToNum.find(str) != cityToNum.end()) {//map里面有 
		return cityToNum[str];
	}
	else {//map里没有 
		cityToNum[str] = numCity;
		numToCity[numCity] = str;
		return numCity++;
	}
}

void printPath(int v) {
	if (v == st) {
		cout << numToCity[v];
		return;
	}
	printPath(pre[v]);
	cout << "->" << numToCity[v];
}

int main() {
	string str;
	scanf("%d %d", &n, &m);
	fill(G[0], G[0] + maxn * maxn, INF);
	for (int i = 0;i < n;++i) pre[i] = i;
	cin >> str;
	st = change(str);
	for (int i = 0;i < n - 1;++i) {
		cin >> str;
		int city = change(str);
		scanf("%d", &weight[city]);
	}
	for (int i = 0;i < m;++i) {
		string str1, str2;
		int u, v, l;
		cin >> str1 >> str2 >> l;
		u = change(str1);
		v = change(str2);
		G[u][v] = G[v][u] = l;
	}
	Dijkstra(st);
	int ed = cityToNum["ROM"];
	printf("%d %d %d %d\n", num[ed], d[ed], w[ed], w[ed] / pt[ed]);
	printPath(ed);
	return 0;
}

《算法笔记》10.6小节——图算法专题->拓扑排序

算法7-12:有向无环图的拓扑排序

#include<cstdio>
#include<cstring>
#include<stack>
#include<vector>
using namespace std;
const int maxn = 55;
const int INF = 1 << 30 - 1;
int n;
int G[maxn][maxn], inDegree[maxn] = { 0 };
vector<int> vi;

bool topologicalSort() {
	int num = 0;
	stack<int> st;
	for (int i = 0;i < n;++i) {
		if (inDegree[i] == 0) st.push(i);
	}
	while (!st.empty()) {
		int top = st.top();
		st.pop();
		vi.push_back(top);
		for (int i = 0;i < n;++i) {
			if (G[top][i] == 1) {
				--inDegree[i];
				if (inDegree[i] == 0) {
					st.push(i);
				}
			}
		}
		++num;
	}
	if (num == n) return true;
	else return false;
}

int main() {
	while (scanf("%d", &n) != EOF) {
		vi.clear();
		memset(inDegree, false, sizeof(inDegree));
		for (int i = 0;i < n;++i) {
			for (int j = 0;j < n;++j) {
				scanf("%d", &G[i][j]);
				if (G[i][j] == 1) ++inDegree[j];
			}
		}
		if (topologicalSort()) {
			for (int i = 0;i < vi.size();++i) {
				printf("%d ", vi[i]);
			}
			printf("\n");
		}
		else {
			printf("ERROR\n");
		}
	}
	return 0;
}

确定比赛名次

#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 510;
const int INF = 1 << 30 - 1;
int n, m;
vector<int> G[maxn];
vector<int> res;
int inDegree[maxn] = { 0 };

void topologicalSort() {
	priority_queue<int, vector<int>, greater<int> > qu;
	for (int i = 1;i <= n;++i) {
		if (inDegree[i] == 0) {
			qu.push(i);
		}
	}
	while (!qu.empty()) {
		int t = qu.top();
		qu.pop();
		res.push_back(t);
		for (int i = 0;i < G[t].size();++i) {
			--inDegree[G[t][i]];
			if (inDegree[G[t][i]] == 0) {
				qu.push(G[t][i]);
			}
		}
		G[t].clear();
	}
}

int main() {
	while (scanf("%d %d", &n, &m), n || m) {
		for (int i = 1;i <= n;++i) {
			if (G[i].size() > 0) G[i].clear();
		}
		memset(inDegree, 0, sizeof(inDegree));
		res.clear();
		for (int i = 0;i < m;++i) {
			int u, v;
			scanf("%d %d", &u, &v);
			G[u].push_back(v);
			++inDegree[v];
		}
		topologicalSort();
		for (int i = 0;i < res.size();++i) {
			if (i > 0) printf(" ");
			printf("%d", res[i]);
		}
		printf("\n");
	}
	return 0;
}

Legal or Not

又是一道用矩阵不给过的题,原因未知。

#include<cstdio>
#include<cstring>
#include<vector> 
#include<queue>
using namespace std;
const int maxn = 110;
int inDegree[maxn];
int n, m;
vector<int> G[maxn];

void topologicalSort() {
	queue<int> qu;
	int num = 0;
	for (int i = 0;i < n;++i) {
		if (inDegree[i] == 0) {
			qu.push(i);
		}
	}
	while (!qu.empty()) {
		int u = qu.front();
		qu.pop();
		for (int i = 0;i < G[u].size();++i) {
			int v = G[u][i];
			--inDegree[v];
			if (inDegree[v] == 0) {
				qu.push(v);
			}
		}
		++num;
	}
	if (num == n) printf("YES\n");
	else printf("NO\n");
}

int main() {
	while (scanf("%d %d", &n, &m) && n) {
		memset(inDegree, 0, sizeof(inDegree));
		for (int i = 0;i < n;++i) {
			if (G[i].size() > 0) G[i].clear();
		}
		for (int i = 0;i < m;++i) {
			int u, v;
			scanf("%d %d", &u, &v);
			G[u].push_back(v);
			++inDegree[v];
		}
		topologicalSort();
	}
	return 0;
}

《算法笔记》10.7小节——图算法专题->关键路径

关键路径

这题的易错点是起点未定,因此代码中自己找到起点,并将路径记录在一个vector中,不断迭代求出路径。另外还要注意当有多组数据的时候一定要将要重复使用的容器清空。

#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<stack>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn = 20;
struct node {
	int v, w;
	node(int _v, int _w) : v(_v), w(_w) {}
};

int n, m;
int inDegree[maxn], ve[maxn], vl[maxn];
map<int, char> intToChar;
map<char, int> charToInt;
vector<node> G[maxn];
vector<int> path[maxn];
stack<int> topOrder;

void topologicalSort() {
	memset(ve, 0, sizeof(ve));
	queue<int> qu;
	for (int i = 0;i < n;++i) {
		if (inDegree[i] == 0) {
			qu.push(i);
		}
	}
	while (!qu.empty()) {
		int u = qu.front();
		qu.pop();
		topOrder.push(u);
		for (int i = 0;i < G[u].size();++i) {
			int v = G[u][i].v;
			--inDegree[v];
			if (inDegree[v] == 0) {
				qu.push(v);
			}
			if (ve[v] < ve[u] + G[u][i].w) {
				ve[v] = ve[u] + G[u][i].w;
			}
		}
	}
}

void CriticalPath() {
	int maxLenth = 0;
	for (int i = 0;i < n;++i) {
		if (ve[i] > maxLenth) {
			maxLenth = ve[i];
		}
	}
	fill(vl, vl + maxn, maxLenth);
	while (!topOrder.empty()) {
		int u = topOrder.top();
		topOrder.pop();
		for (int i = 0;i < G[u].size();++i) {
			int v = G[u][i].v;
			if (vl[v] - G[u][i].w < vl[u]) {
				vl[u] = vl[v] - G[u][i].w;
			}
		}
	}
	int st;
	for (int i = 0;i < maxn;++i) {
		path[i].clear();//清空path
	}
	for (int u = 0;u < n;++u) {
		if (ve[u] == 0) st = u;
		for (int i = 0;i < G[u].size();++i) {
			int v = G[u][i].v, w = G[u][i].w;
			int e = ve[u], l = vl[v] - w;//活动的最早开始时间e和最迟开始时间l
			if (e == l) {
				path[u].push_back(v);//将u->v放入path中 
			}
		}
	}
	for (int u = st;path[u].size();) {//找起点
		int v = path[u][0];
		printf("(%c,%c) ", intToChar[u], intToChar[v]);
		u = v;
	}
	printf("%d\n", maxLenth);
}

int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		memset(inDegree, 0, sizeof(inDegree));
		intToChar.clear();
		charToInt.clear();
		for (int i = 0;i < maxn;++i) {
			G[i].clear();
		}
		scanf("%d %d", &n, &m);
		getchar();
		for (int i = 0;i < n;++i) {
			char c;
			scanf("%c", &c);
			charToInt[c] = i;
			intToChar[i] = c;
		}
		for (int i = 0;i < m;++i) {
			//	getchar();
			char c, s;
			int l;
			scanf("%*c%c %c %d", &c, &s, &l);
			int u = charToInt[c], v = charToInt[s];
			G[u].push_back(node(v, l));
			++inDegree[v];
		}
		topologicalSort();
		CriticalPath();
	}
	return 0;
}

后面的PAT貌似不考暂时搁置。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值