7-35 城市间紧急救援 (25 分)

题目链接

提供个样例:

输入 
8 12 0 7
10 20 30 90 40 30 10 10
0 1 5
1 5 3
5 7 6
0 3 7
3 7 7
0 2 3
2 3 10
1 2 2
3 6 5
2 4 7
4 6 2
6 7 2
输出 
5 120
0 3 6 7

其中5条最短路径(长14)分别为:
0 1 5 7
0 2 1 5 7
0 3 7
0 3 6 7
0 2 4 6 7

普通Dijkstra:

#include<iostream>
#include<queue>
#include<stack>
#include<cstring>
using namespace std;
const int N = 510, INF = 0x3f3f3f3f;
int g[N][N];
int num[N],w[N],count[N],dis[N],path[N];
bool book[N];
int n,m,s,d;
void Dijkstra(){
    path[s] = -1;
    count[s] = 1;
    w[s] = num[s];
    dis[s] = 0;
    for(int i = 0; i < n; i++){
    	int minn = INF, u = -1;
    	for(int i = 0; i < n; i++){
    		if(!book[i] && minn > dis[i]){
    			minn = dis[i];
    			u = i;
			}
		}
		book[u] = true;
		for(int i = 0; i < n; i++){
			if(!book[i] && g[i][u] != INF){
				if(dis[i] > g[i][u] + dis[u]){
					dis[i] = g[i][u] + dis[u];
					count[i] = count[u];
					w[i] = num[i] + w[u];
					path[i] = u;
				} else if(dis[i] == g[i][u] + dis[u]) {
					count[i] += count[u];
					if(w[i] < w[u] + num[i]){
						w[i] = w[u] + num[i];
						path[i] = u;
					}
				}
			}
		} 
	}
}
int main(){
    cin>>n>>m>>s>>d;
    memset(g,0x3f,sizeof(g));
    memset(dis,0x3f,sizeof(dis));
    for(int i = 0; i < n; i++) {
        cin>>num[i];
    }
    for(int i = 0; i < m; i++){
        int u,v,w;
        cin>>u>>v>>w;
        g[u][v] = g[v][u] = w;
    }
    Dijkstra();
    cout<<count[d]<<' '<<w[d]<<endl;
    int t = d;
    stack<int> pathh;
    while(t != -1){
        pathh.push(t);
        t = path[t];
    }
    while(pathh.size()){
    	int x = pathh.top(); pathh.pop();
        if(x != s) cout<<' ';
        cout<<x;
    }
    return 0;
}

堆优化Dijkstra:比普通的还慢,还出现一堆问题导致调试了一天无语死,就nm离谱

#include<iostream>
#include<queue>
#include<stack>
#include<vector>
#include<cstring>
using namespace std;
const int N = 510, INF = 0x3f3f3f3f;
struct Edge{
    int v,w;
};
struct Node{
    int dis;//到起点距离
    int amount;//人数
    int num;//当前点编号
    bool friend operator <(Node a,Node b){
        if(a.dis != b.dis) return a.dis > b.dis;//距离小的优先
        else if(a.amount != b.amount) return a.amount < b.amount;//人数大的优先
        return a.num > b.num;
    } 
};
priority_queue<Node> que;
vector<Edge> g[N];
int num[N],w[N],count[N],dis[N],path[N];
bool book[N];
int n,m,s,d;
void Dijkstra(){
    path[s] = -1;
    count[s] = 1;//默认一条最短路径
    w[s] = num[s];
    dis[s] = 0;
    book[s] = true;
    que.push({dis[s],w[s],s});
    while(que.size()){
        int x = que.top().num;
        int len = que.top().dis;
        int weight = que.top().amount;
        que.pop();
        //改方法会加入一些垃圾(多余的)到队列中,需要筛选
        //len只会>=dis[x],大于说明之前加入队列是多余的,等于就是需要松弛的点。
        //w[x] > weight,其中大于说明已经更新过(在距离相同情况下,人数多的会优先更新),之前加入队列是多余的。
        if(len > dis[x] || w[x] > weight) continue;//之前由于少了w[x] > weight导致路径数count重复增加
        for(auto tmp:g[x]){
            int i = tmp.v;
            if(dis[i] > tmp.w + dis[x]){
                dis[i] = tmp.w + dis[x];
                count[i] = count[x];
                w[i] = w[x]+num[i];
                path[i] = x;
                que.push({dis[i],w[i],i});
            } else if(dis[i] == tmp.w + dis[x]) {
                count[i] += count[x];
                if(w[i] < w[x]+num[i]){
                    w[i] = w[x]+num[i];
                    path[i] = x;
                    que.push({dis[i],w[i],i});
                }
            }
        }
    }
}
int main(){
    cin>>n>>m>>s>>d;
    fill(dis,dis+N,INF);
    for(int i = 0; i < n; i++) {
        cin>>num[i];
    }
    for(int i = 0; i < m; i++){
        int u,v,w;
        cin>>u>>v>>w;
        //邻接表
        g[u].push_back({v,w});
        g[v].push_back({u,w});
    }
    Dijkstra();
    cout<<count[d]<<' '<<w[d]<<endl;
    int t = d;
    stack<int> pathh;
    while(t != -1){
        pathh.push(t);
        t = path[t];
    }
    while(pathh.size()){
    	int x = pathh.top(); pathh.pop();
        if(x != s) cout<<' ';
        cout<<x;
    }
    return 0;
}

dfs做法:剪枝后错了,输入上面样例程序会少算一条0,2,1,5,7的最短路径(因为1锁定后,2不能往1那边跑导致2到7的最短路径会少了2,1,5,7)。

dfs的代码调试一天给我心态整崩了,一直不想调试上面的样例(太多了,确实犹豫就会败北),经过使用上面的样例来艰苦的调试之后发现问题(要是早就这样调试也不用浪费这么多时间了)。

#include<iostream>
#include<cstring>
using namespace std;
const int N = 510, INF = 0x3f3f3f3f;
int num[N],path[N];
bool book[N];
int vis[N][N],a[N][N],dn[N][N];//vis i->j距离 a i->j人数 dn i->j最短路径数量 
int g[N][N];//地图 
int n,m,s,d,minlen = INF,sum;//sum救援队数量
bool dfs(int x,int len,int amount){//当前点,长度,救援队人数 
    if(x == d){
		if(len < minlen || len == minlen && sum < amount){
        	return true;//路径有更新 
        }
        return false;
    }
    if(vis[x][d] != INF) return false;//剪枝  
    int point = -1;//x的下一个点 
    int minn = INF;//x到d的最短路径 
    for(int i = 0; i < n; i++){
        if(!book[i] && g[x][i]!=INF && minlen >= len+g[i][x]){
            book[i] = true;
			if(vis[i][d] != INF && len + g[x][i] + vis[i][d] < minlen) {//剪枝 
            	dn[x][d] = dn[i][d];
	            minlen = len + g[x][i] + vis[i][d];
	            minn = g[x][i] + vis[i][d];
	            sum = amount + a[i][d];
	            a[x][d] = a[i][d] + num[x]; 
	            point = i;
			} else if(vis[i][d] != INF && len + g[x][i] + vis[i][d] == minlen){//剪枝 
				dn[x][d] += dn[i][d];
				minn = g[x][i] + vis[i][d];
				if(sum < amount + a[i][d]){
	                sum = amount + a[i][d];
	                point = i;
	            }
			} else if(vis[i][d] == INF) {
			 	if(dfs(i,len+g[x][i],amount+num[i])) {//路径或人数有更新 
			 		point = i;
			 		a[x][d] = a[i][d] + num[x];
				}
				if(minn == g[x][i] + vis[i][d]) {
					dn[x][d] += dn[i][d];
				} else if(minn > g[x][i] + vis[i][d]) {
					minn = g[x][i] + vis[i][d];
					dn[x][d] = dn[i][d];
				}
			}
            book[i] = false;
        }
    }
    if(point != -1) {
    	path[x] = point;
    	vis[x][d] = minn;
    	return true;
	} 
	return false;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m>>s>>d;
    memset(g,0x3f,sizeof(g));
    memset(vis,0x3f,sizeof(vis));
    for(int i = 0; i < n; i++) {
        cin>>num[i];
    }
    for(int i = 0; i < m; i++){
        int u,v,w;
        cin>>u>>v>>w;
        g[u][v] = g[v][u] = w;
    }
     
    book[s] = true;
    path[d] = -1;
	dn[d][d] = 1;//默认最短路径为一条 
	vis[d][d] = 0;
	a[d][d] = num[d];
    dfs(s,0,num[s]);
    cout<<dn[s][d]<<' '<<sum<<endl;
    int t = s;
    while(t != -1){
        if(t!=s) cout<<' ';
        cout<<t;
        t = path[t];
    }
    return 0;
}

 2022.4.20标程

#include<iostream>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#define d first
#define n second
using namespace std;
const int N = 1010;
typedef pair<int,int> PII;
struct Edge{
	int to,d; 
};
vector<vector<Edge>> g(N);
//cnt[i]为s到i的最短路径条数,pn[i]为s到i的最多人数 
int w[N],path[N],dis[N],cnt[N],pn[N];
int n,m,s,d;
bool st[N];
void djkstra(){
	memset(path,-1,sizeof path);
	memset(dis,0x3f,sizeof dis);
	priority_queue<PII,vector<PII>,greater<PII>> que;
	que.push({0,s});
	dis[s] = 0; pn[s] = w[s]; cnt[s] = 1;
	while(que.size()){
		auto tmp = que.top();
		que.pop();
		const int &u = tmp.n;
		if(st[u]) continue;
		st[u] = true;
		for(auto edge: g[u]){
			const int &v = edge.to, &dd = edge.d;
			if(!st[v]){
				if(dis[v] > dis[u] + dd){
					dis[v] = dis[u] + dd;
					path[v] = u;
					cnt[v] = cnt[u];
					pn[v] = w[v] + pn[u];
					que.push({dis[v],v});
				} else if(dis[v] == dis[u] + dd){
					cnt[v] += cnt[u];
					if(pn[v] < pn[u] + w[v]){
						pn[v] = pn[u] + w[v];
						path[v] = u;
					}
				}
			}
		}
	}
	
	cout<<cnt[d]<<' '<<pn[d]<<'\n';
	stack<int> sk;
	int t = d;
	while(t != -1) {
		sk.push(t);
		t = path[t];
	}
	while(sk.size()) {
		cout<<sk.top();
		sk.pop();
		if(sk.size()) cout<<' ';
	}	
	
}

int main(){
	
	cin>>n>>m>>s>>d;
	for(int i = 0; i < n; i++) cin>>w[i];
	for(int i = 0; i < m; i++){
		int u,v,d;
		cin>>u>>v>>d;
		g[u].push_back({v,d});
		g[v].push_back({u,d});
	}
	
	djkstra();
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值