算法课作业——贪心法

返回主目录

2-1 旅游规划 (25分)

题目描述

有了一张自驾旅游路线图,你会知道城市间的高速公路长度、以及该公路要收取的过路费。现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径。如果有若干条路径都是最短的,那么需要输出最便宜的一条路径。

输入格式:
输入说明:输入数据的第1行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0~(N−1);M是高速公路的条数;S是出发地的城市编号;D是目的地的城市编号。随后的M行中,每行给出一条高速公路的信息,分别是:城市1、城市2、高速公路长度、收费额,中间用空格分开,数字均为整数且不超过500。输入保证解的存在。

输出格式:
在一行里输出路径的长度和收费总额,数字间以空格分隔,输出结尾不能有多余空格。

输入样例:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
输出样例:
3 40

代码实现

#include<bits/stdc++.h>
using namespace std;
int N, M, S, D;
#define MAX 99999

int minInSP(vector<int> shortestPath,vector<bool> Selected) {
	//返回shortestPath中最小的值的索引
	int res, min = MAX;
	for (int i = 0; i < shortestPath.size(); i++) {	//需要从0开始扫,因为开始的城市不一定在0
		if (!Selected[i]&&shortestPath[i]<min) {
			res = i;
			min = shortestPath[i];
		}
	}
	return res;
}

void dijkstra( const vector<vector<int> >&tableOfDist, const vector<vector<int> >&tableOfCost, int &ansLength, int &ansCost) {
	vector<int> shortestPath(N);
	vector<int> minCost(N);
	vector<bool> Selected(N);	//true表示被选中加入集合,false则还没有,对应书里就是有没有加入X集合
	Selected[S] = true;

	for (int i = 0; i < N; i++) {		//初始化
		if (i != S) {
			if (tableOfDist[S][i]) {
				shortestPath[i] = tableOfDist[S][i];
				minCost[i] = tableOfCost[S][i];
			}
			else {
				shortestPath[i] = MAX;
				minCost[i] = MAX;
			}
		}
	}
	for (int i = 0; i < N - 1; i++) {
		int y = minInSP(shortestPath, Selected);
		Selected[y] = true;
		for (int w = 0; w < N; w++) {
			if (!Selected[w] && tableOfDist[y][w]) {	//if没被选中,且y与w有边
				if (shortestPath[y] + tableOfDist[y][w] == shortestPath[w]&& minCost[y] + tableOfCost[y][w] < minCost[w]) {
					minCost[w] = minCost[y] + tableOfCost[y][w];
				}
				else if (shortestPath[y] + tableOfDist[y][w] < shortestPath[w]) {
					shortestPath[w] = shortestPath[y] + tableOfDist[y][w];
					minCost[w] = minCost[y] + tableOfCost[y][w];
				}
			}
		}
	}
	ansLength = shortestPath[D];
	ansCost = minCost[D];
}

int main() {
	cin >> N >> M >> S >> D;
	vector<vector<int> >tableOfDist(N,vector<int>(N));	//存储距离图
	vector<vector<int> >tableOfCost(N, vector<int>(N));	//存储代价图
	int s, e, d, c;
	for (int i = 0; i < M; i++) {
		cin >> s >> e >> d >> c;
		tableOfDist[s][e] = tableOfDist[e][s] = d;
		tableOfCost[s][e] = tableOfCost[e][s] = c;
	}
	int ansLength, ansCost;
	dijkstra( tableOfDist, tableOfCost, ansLength, ansCost);
	cout << ansLength << ' ' << ansCost;
	return 0;
}

2-2 直捣黄龙 (30分)

题目描述

本题是一部战争大片 —— 你需要从己方大本营出发,一路攻城略地杀到敌方大本营。首先时间就是生命,所以你必须选择合适的路径,以最快的速度占领敌方大本营。当这样的路径不唯一时,要求选择可以沿途解放最多城镇的路径。若这样的路径也不唯一,则选择可以有效杀伤最多敌军的路径。

输入格式:
输入第一行给出2个正整数N(2 ≤ N ≤ 200,城镇总数)和K(城镇间道路条数),以及己方大本营和敌方大本营的代号。随后N-1行,每行给出除了己方大本营外的一个城镇的代号和驻守的敌军数量,其间以空格分隔。再后面有K行,每行按格式城镇1 城镇2 距离给出两个城镇之间道路的长度。这里设每个城镇(包括双方大本营)的代号是由3个大写英文字母组成的字符串。

输出格式:
按照题目要求找到最合适的进攻路径(题目保证速度最快、解放最多、杀伤最强的路径是唯一的),并在第一行按照格式己方大本营->城镇1->…->敌方大本营输出。第二行顺序输出最快进攻路径的条数、最短进攻距离、歼敌总数,其间以1个空格分隔,行首尾不得有多余空格。

输入样例:
10 12 PAT DBY
DBY 100
PTA 20
PDS 90
PMS 40
TAP 50
ATP 200
LNN 80
LAO 30
LON 70
PAT PTA 10
PAT PMS 10
PAT ATP 20
PAT LNN 10
LNN LAO 10
LAO LON 10
LON DBY 10
PMS TAP 10
TAP DBY 10
DBY PDS 10
PDS PTA 10
DBY ATP 10
输出样例:
PAT->PTA->PDS->DBY
3 30 210

代码实现

#include<bits/stdc++.h>
using namespace std;
#define MAX 0x3f3f3f3f
int N, K;
string ourCamp, enemyCamp;
map<int, string> numToTown;
map<string, int> townToNum;

int minInSP(vector<int> shortestPath, vector<bool> Selected) {
	//返回shortestPath中最小的值的索引
	int res, min = MAX;
	for (int i = 0; i < shortestPath.size(); i++) {	//需要从0开始扫,因为开始的城市不一定在0
		if (!Selected[i] && shortestPath[i] < min) {
			res = i;
			min = shortestPath[i];
		}
	}
	return res;
}

void dfs(int index, int &town, int &enemy, const vector<int> &enemyInTown, const vector<int> &father) {
	//求到达index的目前最适合路径的城镇数,敌人总数
	if (index == 0) return;
	town++;
	enemy += enemyInTown[index];
	dfs(father[index], town, enemy, enemyInTown, father);
}

vector<int> dijkstra(const vector<vector<int> > &myMap, const vector<int> &enemyInTown, int &numOfRoute) {
	vector<bool> selected(N);
	selected[0] = true;
	vector<int> shortestDist(N);
	shortestDist[0] = 0;
	vector<int> father(N);	//记录某点的上一个点
	vector<int> numOfNR(N);	//到某点的最近的路有几条
	for (int i = 1; i < N; i++) {
		if (myMap[0][i] != MAX) {
			shortestDist[i] = myMap[0][i];
			father[i] = 0;
			numOfNR[i] = 1;
		}
		else {
			shortestDist[i] = MAX;
			father[i] = -1;
			numOfNR[i] = 0;
		}
	}
	for (int i = 0; i < N - 1; i++) {
		int y = minInSP(shortestDist, selected);
		selected[y] = true;
		for (int w = 1; w < N; w++) {
			if (!selected[w] && myMap[y][w] != MAX) {
				int tmp = shortestDist[y] + myMap[y][w];
				if (tmp == shortestDist[w]) {
					numOfNR[w] = numOfNR[w] + numOfNR[y];
					int originRouteTown = 0, originRouteEnemy = 0, tmpRouteTown = 0, tmpRouteEnemy = 0;
					dfs(father[w], originRouteTown, originRouteEnemy, enemyInTown, father);
					dfs(y, tmpRouteTown, tmpRouteEnemy, enemyInTown, father);
					if (originRouteTown < tmpRouteTown) {
						father[w] = y;
					}
					else if (originRouteTown == tmpRouteTown) {
						if (originRouteEnemy < tmpRouteEnemy) {	//题目保证有解,就一个if就行了
							father[w] = y;
						}
					}
				}
				else if (tmp < shortestDist[w]) {
					shortestDist[w] = tmp;
					father[w] = y;
					numOfNR[w] = numOfNR[y];
				}
			}
		}
	}
	numOfRoute = numOfNR[townToNum[enemyCamp]];
	return father;
}

void dfs2(int index, stack<string> &finalRoute, int &ansDist, int &ansEnemy, const vector<int> &father, const vector<int>& enemyInTown, const vector<vector<int> > &myMap) {
	//与上面的dfs()不一样在多了栈存储路径
	if (index == 0)return;
	finalRoute.push(numToTown[index]);
	ansDist += myMap[father[index]][index];
	ansEnemy += enemyInTown[index];
	dfs2(father[index], finalRoute, ansDist, ansEnemy, father, enemyInTown, myMap);
}

int main() {
	cin >> N >> K >> ourCamp >> enemyCamp;
	string tmpStr; int tmpInt;
	vector<int> enemyInTown(N);
	vector<vector<int> > myMap(N, vector<int>(N));
	numToTown[0] = ourCamp;
	townToNum[ourCamp] = 0;
	for (int i = 0; i < N; i++) {	//初始化图
		for (int j = 0; j < N; j++) {
			myMap[i][j] = MAX;
		}
	}
	for (int i = 1; i < N; i++) {	//0留给出发地,总共n-1次
		cin >> tmpStr >> tmpInt;
		numToTown[i] = tmpStr;
		townToNum[tmpStr] = i;
		enemyInTown[i] = tmpInt;
	}
	string tmpStr1;
	for (int i = 0; i < K; i++) {	//输入图
		cin >> tmpStr >> tmpStr1 >> tmpInt;
		myMap[townToNum[tmpStr]][townToNum[tmpStr1]] = myMap[townToNum[tmpStr1]][townToNum[tmpStr]] = tmpInt;
	}
	int numOfRoute;	//答案所求的最快进攻路径数,也就是多少条最短路径
	vector<int> father = dijkstra(myMap, enemyInTown, numOfRoute);
	stack<string> finalRoute;
	int ansDist = 0, ansEnemy = 0;
	dfs2(townToNum[enemyCamp], finalRoute, ansDist, ansEnemy, father, enemyInTown, myMap);
	cout << ourCamp;
	while (!finalRoute.empty()) {
		cout << "->" << finalRoute.top();
		finalRoute.pop();
	}
	cout << endl;
	cout << numOfRoute << ' ' << ansDist << ' ' << ansEnemy;
	return 0;
}

2-3 最小生成树的唯一性 (35分)

题目描述

给定一个带权无向图,如果是连通图,则至少存在一棵最小生成树,有时最小生成树并不唯一。本题就要求你计算最小生成树的总权重,并且判断其是否唯一。

输入格式:
首先第一行给出两个整数:无向图中顶点数 N(≤500)和边数 M。随后 M 行,每行给出一条边的两个端点和权重,格式为“顶点1 顶点2 权重”,其中顶点从 1 到N 编号,权重为正整数。题目保证最小生成树的总权重不会超过 2
​30
​​ 。

输出格式:
如果存在最小生成树,首先在第一行输出其总权重,第二行输出“Yes”,如果此树唯一,否则输出“No”。如果树不存在,则首先在第一行输出“No MST”,第二行输出图的连通集个数。

输入样例 1:
5 7
1 2 6
5 1 1
2 3 4
3 4 3
4 1 7
2 4 2
4 5 5
输出样例 1:
11
Yes
输入样例 2:
4 5
1 2 1
2 3 1
3 4 2
4 1 2
3 1 3
输出样例 2:
4
No
输入样例 3:
5 5
1 2 1
2 3 1
3 4 2
4 1 2
3 1 3
输出样例 3:
No MST
2

代码实现

#include<bits/stdc++.h>
using namespace std;
//#define MAX 0x3f3f3f3f
int N, M, ansWeight = 0, numOfConnectSet = 0;
vector<int> indexOfOverlapEdge;

struct edge {
	int u, v, weight;
	edge(int a, int b, int w) {
		this->u = a;
		this->v = b;
		this->weight = w;
	}
};

bool cmp(const edge a, const edge b) {
	return a.weight < b.weight;
}

int getFather(int index,vector<int> &father) {
	if (index == father[index])
		return index;
	else {
		return getFather(father[index], father);
	}
}

vector<int> myKruskal(vector<edge> &setOfEdge) {
	sort(setOfEdge.begin(), setOfEdge.end(), cmp);
	vector<int> father(N + 1);
	for (int i = 1; i <= N; i++) {	//初始化father
		father[i] = i;
	}
	int counter = 0;	//记录已经加入多少条
	for (int i = 0; i < M; i++) {
		if (counter == N - 1)break;	//
		int fatherOfU = getFather(setOfEdge[i].u, father);
		int fatherOfV = getFather(setOfEdge[i].v, father);
		if (fatherOfU != fatherOfV) {
			counter++;
			if (fatherOfU < fatherOfV)
				father[fatherOfV] = fatherOfU;
			else
				father[fatherOfU] = fatherOfV;
			ansWeight += setOfEdge[i].weight;
			if ((i < M - 1 && setOfEdge[i].weight == setOfEdge[i + 1].weight))
			{	//判断是否存在重叠边,因为已经排序了,所以如果存在则i和i+1的权重会相等
				indexOfOverlapEdge.push_back(i);
			}
		}
	}
	return father;
}

bool checkUniqueness(vector<int> father, vector<edge> setOfEdge) {
	//唯一返回true,不唯一返回false
	//检查唯一性:当删去权值相同的边时,还能不能构成最小生成树
	//实际上,就是加了检查功能的kruskal算法
	for (int i = 0; i < indexOfOverlapEdge.size(); i++) {	//每个重复边会检查一次
		for (int i = 1; i <= N; i++) {	
			father[i] = i;
		}
		int tmpWeight = 0, edges = 0, counter = 0;
		for (int j = 0; j < M; j++) {
			if (j == indexOfOverlapEdge[i])
				continue;
			if (counter == N - 1)break;
			int fatherOfU = getFather(setOfEdge[j].u, father);
			int fatherOfV = getFather(setOfEdge[j].v, father);
			if (fatherOfU != fatherOfV) {
				if (fatherOfU < fatherOfV)
					father[fatherOfV] = fatherOfU;
				else
					father[fatherOfU] = fatherOfV;
				tmpWeight += setOfEdge[j].weight;
				edges++;
				counter++;
			}
		}
		if (edges == N - 1 && tmpWeight == ansWeight)return false;	//一旦出现,说明存在另一个最小生成树
	}
	return true;

}

int main() {
	cin >> N >> M;
	vector<edge> setOfEdge;
	int tmpU, tmpV, tmpWeight;
	for (int i = 0; i < M; i++) {	//输入图
		cin >> tmpU >> tmpV >> tmpWeight;
		setOfEdge.push_back(*new edge(tmpU, tmpV, tmpWeight));
	}
	vector<int> father = myKruskal(setOfEdge);
	for (int i = 1; i <= N; i++) {	//多少个father,多少个连通集
		if (getFather(i,father) == i) {
			numOfConnectSet++;
		}
	}
	if (numOfConnectSet != 1) {
		cout << "No MST" << endl << numOfConnectSet;
	}
	else {
		cout << ansWeight << endl;
		if (indexOfOverlapEdge.empty() || checkUniqueness(father, setOfEdge))	//存在重复边就进一步检查唯一性,不存在则必唯一
			cout << "Yes";
		else
			cout << "No";
	}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值