简单做个刷题记录,涉及的知识点是:最小生成树、最短路径
网络延迟时间
https://leetcode-cn.com/problems/network-delay-time/
思路:可以当做最短路径的模板题,采用DisjKstra算法和 Floyd算法都可以,不了解的可以看一下王卓老师的教学PPT
DisjKstra算法:
Floyd算法:
解法一:DisjKstra算法
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
//构建有向图
vector<vector<long long>> graph(N+1,vector<long long>(N+1,INT_MAX));//用 long long类型是防止溢出,如 INT_MAX+2
//使用邻接矩阵存储邻接关系
for(int i = 0;i<times.size();i++){
graph[times[i][0]][times[i][1]] = times[i][2];
}
for(int i = 1;i<=N;i++){
graph[i][i] = 0;
}
vector<bool> visited(N+1,false);
visited[K] = true;
for(int i = 1;i<N;i++){
int index = 0;
int dis = INT_MAX;//每次在未访问的点中找到距离K最近的那个
for(int j = 1;j<=N;j++){
if(visited[j]==false&&graph[K][j]<dis){
dis = graph[K][j];
index = j;
}
}
visited[index] = true;
//加入这个点后,更新最短路径图
for(int j = 1;j<=N;j++){
if(graph[K][index]+graph[index][j]<graph[K][j]){
graph[K][j] = graph[K][index]+graph[index][j];
}
}
}
int res = 0;
for(int i = 1;i<=N;i++){
if(graph[K][i]==INT_MAX){
return -1;
}
res = max(res,(int)graph[K][i]);//max比较的两个数必须类型相同,因此要用类型转换
}
return res;
}
};
解法二:Floyd算法
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
//构建有向图
vector<vector<long long>> graph(N+1,vector<long long>(N+1,INT_MAX));//用 long long类型是防止溢出,如 INT_MAX+2
for(int i = 0;i<times.size();i++){
graph[times[i][0]][times[i][1]] = times[i][2];
}
for(int i = 1;i<=N;i++){
graph[i][i] = 0;
}
for(int k = 1;k<=N;k++){
for(int i = 1;i<=N;i++){
for(int j = 1;j<=N;j++){
graph[i][j] = min(graph[i][j],graph[i][k]+graph[k][j]);
}
}
}
int res = 0;
for(int i = 1;i<=N;i++){
if(graph[K][i]==INT_MAX){
return -1;
}
res = max(res,(int)graph[K][i]);//max比较的两个数必须类型相同,因此要用类型转换
}
return res;
}
};
阈值距离内邻居最少的城市
https://leetcode-cn.com/problems/find-the-city-with-the-smallest-number-of-neighbors-at-a-threshold-distance/
思路:Dijkstra 算法。dis数组用于记录两城市间最短距离,遍历每一个城市city[i],通过Dijkstra算法找到城市city[i]到其余(n - 1)个城市的最短距离,并将结果保存在dis[i]中。再通过遍历dis数组,找与城市city[i]的最短距离小于等于threshold distance的城市个数。每次比较以某一个城市为起点且最短路径距离小于等于threshold distance最终得到的城市个数,找到最大编号,即为所求
class Solution {
public:
int findTheCity(int n, vector<vector<int>>& edges, int distanceThreshold) {
int Max = 0xfff;
vector<vector<int>> graph(n,vector(n,-1));
for(int i = 0;i<edges.size();i++){
graph[edges[i][0]][edges[i][1]]=edges[i][2];
graph[edges[i][1]][edges[i][0]]=edges[i][2];
}
int res = 0;
//每个结点作为源,求到其他顶点的最短路径
for(int i = 0;i<n;i++){
vector<int> dis (n,INT_MAX);
dis[i] = 0;
queue<int> q;
for(int j = 0;j<n;j++){
if(graph[i][j]!=-1){
dis[j] = graph[i][j];//把直接相连的顶点长度值存储到dis数组中
q.push(j);
}
}
while(!q.empty()){
int t = q.front();
q.pop();
for(int j = 0;j<n;j++){
if(graph[t][j]!=-1){
if(dis[t]+graph[t][j]<dis[j]&&dis[t]+graph[t][j]<=distanceThreshold){
dis[j] = dis[t]+graph[t][j];
q.push(j);
}
}
}
}
int val = 0;
for(int j = 0;j<n;j++){
if(j!=i&&dis[j]>0&&dis[j]<=distanceThreshold)
val++;//含有的节点数量
}
if(val<=Max){
Max = val;
res = i;
}
}
return res;
}
};
课程安排 IV
https://leetcode-cn.com/problems/course-schedule-iv/
思路:Floyd算法。直接套模板。
class Solution {
public:
vector<bool> checkIfPrerequisite(int n, vector<vector<int>>& prerequisites, vector<vector<int>>& queries) {
vector<vector<int>> graph(n,vector<int>(n,0xfff));
for(int i = 0;i<n;i++){
graph[i][i] = 0;
}
for(int j = 0;j<prerequisites.size();j++){
graph[prerequisites[j][0]][prerequisites[j][1]] = 1;
}
//Floyd算法
for(int k = 0;k<n;k++){
for(int i = 0;i<n;i++){
for(int j = 0;j<n;j++){
if(graph[i][j]>graph[i][k]+graph[k][j]){
graph[i][j] = graph[i][k]+graph[k][j];
}
}
}
}
vector<bool> res(queries.size(),false);
for(int i = 0;i<queries.size();i++){
res[i] = graph[queries[i][0]][queries[i][1]]!=0xfff;
}
return res;
}
};
K 站中转内最便宜的航班
https://leetcode-cn.com/problems/cheapest-flights-within-k-stops/
思路:基于动态规划的Bellman-Ford算法。设dp(k, i)表示由src最多经过k个中转站到达i的最低花费。因此可以推知dp(k, i) = min(dp(k-1, i), dp(k-1, j) + w(j, i))
所以要求得dp(k, dst)即要求dp(k-1, dst)和dp(k-1, u)的值
class Solution {
public:
int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) {
vector<long long> dis(n,INT_MAX);
dis[src] = 0;
for(int i = 0;i<=K;i++){
vector<long long> pre = dis;
for(int j = 0;j<flights.size();j++){
dis[flights[j][1]] = min(dis[flights[j][1]],pre[flights[j][0]]+flights[j][2]);
}
}
return (int)dis[dst]==INT_MAX?-1:(int)dis[dst];
}
};
最小生成树模板题
poj的这三道题感觉更适合基础练习(直接套模板 )
Truck History
http://poj.org/problem?id=1789
思路:题目要找到合适的方案使得总代价最小,也就是距离最小(两个编号之间的距离代表这两个编号之间不同字母的个数),假如把每一个字符串当作图的一个顶点,两个字符串的距离当作两点之间的权值,那么问题可以转化为求最小生成树的最小代价问题。因此可用prim算法来求最小生成树从而得到最终结果
#include <iostream>
using namespace std;
int t;
char graph[2001][10];
int w[2001][2001];
int dis[2001];//dis数组是每一个顶点到生成树的任意一个顶点到的最短距离
int visited[1001];
int cal(int f,int s){
int dis = 0;
for(int i = 0;i<7;i++){
if(graph[f][i]!=graph[s][i])
dis++;
}
return dis;
}
//prime算法
int prime(){
int res = 0;
for(int i = 1;i<=t;i++){
dis[i] = INT_MAX;
visited[i] = 0;
}
dis[1] = 0;
for(int i = 1;i<=t;i++){
int index,Min = INT_MAX;
for(int j = 1;j<=t;j++){//从dis数组中找到离生成树最近的顶点下标
if(!visited[j]&&dis[j]<=Min){
Min = dis[j];
index = j;
}
}
visited[index] = 1;
res+=dis[index];
//以index为中间点,更新生成树到每一个非树顶点的距离
for(int k = 1;k<=t;k++){
if(!visited[k]){
dis[k] = min(dis[k],w[index][k]);
}
}
}
return res;
}
int main(){
while(scanf("%d",&t)!=EOF){
if(t==0)
break;
for(int i = 1;i<=t;i++){
scanf("%s",graph[i]);
}
for(int i = 1;i<=t;i++){
for(int j = i+1;j<=t;j++){
w[i][j] = cal(i,j);
w[j][i] = w[i][j];
}
}
int res = prime();
printf("The highest possible quality is 1/%d.\n",res);
}
return 0;
}
Highways
http://poj.org/problem?id=2485
思路:求最小生成树的最大边
Prim算法:
#include <iostream>
using namespace std;
int t,n;
int w[501][501];//代价矩阵
int visited[501];
int dis[501];
int Prim(){
for(int i = 1;i<=n;i++){
dis[i] = w[1][i];
visited[i] = 0;
}
visited[1] = 1;//将第一个顶点加入最小生成树中
int res = -1;
for(int i = 1;i<=n-1;i++){
int Min = INT_MAX;
int index = 0;
for(int j = 1;j<=n;j++){
if(!visited[j]&&dis[j]<Min){//没有在最小生成树中且权值最小的边
Min = dis[j];
index = j;
}
}
res = res>Min?res:Min;
visited[index] = 1;
for(int j = 1;j<=n;j++){
if(!visited[j]&&dis[j]>w[index][j]){
dis[j] = w[index][j];
}
}
}
return res;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i = 1;i<=n;i++){
for(int j = 1;j<=n;j++){
scanf("%d",&w[i][j]);
}
}
int res = Prim();
printf("%d\n",res);
}
return 0;
}
Kruskal算法
#include <iostream>
#include <algorithm>
using namespace std;
int t,n;
int f[505];
struct Node{
int s,e,dis;
}w[1000];
int compare(Node a,Node b){
return a.dis<b.dis;
}
void Init(){
for(int i = 0;i<=n;i++){
f[i] = i;
}
}
//并查集寻找是否是同一个根节点
int find(int num){
if(f[num]==num)
return num;
else{
//路径压缩
f[num] = find(f[num]);
return f[num];
}
}
bool merge(int a,int b){
int t1 = find(a);
int t2 = find(b);
if(t1!=t2){//判断两个点是否在同一个集合当中
f[t2] = t1;
return true;
}
return false;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
int a,k = 0;
for(int i = 1;i<=n;i++){
for(int j = 1;j<=n;j++){
scanf("%d",&a);
if(i<j){//存储一半的数据
w[k].s = i;
w[k].e = j;
w[k].dis = a;
k++;
}
}
}
sort(w+1,w+k+1,compare);//按距离从小到大对边进行排列
//并查集初始化
Init();
int Max = 0;
//Kruskal核心算法部分
for(int i = 1;i<=k;i++){//从小到大枚举每一条边
if(merge(w[i].s,w[i].e)) {
if(Max<w[i].dis)//判断是否是最大边
Max = w[i].dis;
}
}
printf("%d\n",Max);
}
return 0;
}
Agri-Net
http://poj.org/problem?id=1258
思路:求最小生成树的最小代价
//Prime算法
#include <iostream>
#include <climits>
using namespace std;
int t;
int graph[105][105];
int dis[105];
int vis[105];
int Prime(){
for(int i = 1;i<=t;i++){
dis[i] = graph[1][i];
vis[i] = 0;
}
vis[1] = 1;
int res = 0;
for(int i = 1;i<=t-1;i++){
int Min = INT_MAX;
int index = 0;
for(int j = 1;j<=t;j++){
if(!vis[j]&&dis[j]<Min){
Min = dis[j];
index = j;
}
}
res += Min;
vis[index] = 1;
for(int k = 1;k<=t;k++){
if(!vis[k]){
dis[k] = min(dis[k],graph[index][k]);
}
}
}
return res;
}
int main(){
scanf("%d",&t);
for(int i = 1;i<=t;i++){
for(int j = 1;j<=t;j++){
scanf("%d",&graph[i][j]);
}
}
int val = Prime();
printf("%d\n",val);
return 0;
}