最短路(天梯赛练习)

Emergency

1003 Emergency (25 分)

思路:

Dijkstra+更新各种信息

代码:

#include <bits/stdc++.h>
#define f(i,a,b) for(int i=a;i<b;i++)
#define ff(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

const int N=505;
int g[N][N],vis[N],w2[N],d[N];
int w[N],num[N];
int n,m,st,en;

void dij(){
    memset(d,0x3f,sizeof d);
    d[st]=0;
    num[st]=1;
    w[st]=w2[st];//必须设
    f(i,0,n){
        int t=-1;
        f(j,0,n)
            if(!vis[j]&&(t==-1||d[t]>d[j]))t=j;
        vis[t]=1;
        f(j,0,n){
            if(d[j]>d[t]+g[t][j]){
                d[j]=d[t]+g[t][j];
                num[j]=num[t];
                w[j]=w2[j]+w[t];
            }
            else if(d[j]==d[t]+g[t][j]){
                num[j]+=num[t];
                //注意救援队数量越多越好,用"<"
                if(w[j]<w2[j]+w[t]){
                    w[j]=w2[j]+w[t];
                }
            }
        }
    }
}


int main(){
    cin>>n>>m>>st>>en;
    f(i,0,n)cin>>w2[i];
    memset(g,0x3f,sizeof g);
    f(i,0,m){
        int a,b,c;
        cin>>a>>b>>c;
        g[a][b]=g[b][a]=min(g[a][b],c);
    }
    dij();
    cout<<num[en]<<" "<<w[en]<<endl;
}

类似的题目:

L2-001 紧急救援 (25 分)

代码:

#include <bits/stdc++.h>
#define f(i,a,b) for(int i=a;i<b;i++)
#define ff(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

const int N=505;
int g[N][N],vis[N],w2[N],d[N];
int w[N],num[N],pre[N];
int n,m,st,en;

void pri(int t){
    if(t==st){
        cout<<t<<" ";
        return;
    }
    else {
        pri(pre[t]);
        if(t!=en)cout<<t<<" ";
		else cout<<t;
    }
}


void dij(){
    memset(d,0x3f,sizeof d);
    d[st]=0;
    num[st]=1;
    w[st]=w2[st];//必须设
    f(i,0,n){
        int t=-1;
        f(j,0,n)
            if(!vis[j]&&(t==-1||d[t]>d[j]))t=j;
        vis[t]=1;
        f(j,0,n){
            if(d[j]>d[t]+g[t][j]){
                d[j]=d[t]+g[t][j];
                num[j]=num[t];
                w[j]=w2[j]+w[t];
                pre[j]=t;
            }
            else if(d[j]==d[t]+g[t][j]){
                num[j]+=num[t];
				//注意救援队数量越多越好,用"<"
                if(w[j]<w2[j]+w[t]){
                    w[j]=w2[j]+w[t];
                    pre[j]=t;
                }
            }
        }
    }
}


int main(){
	//freopen("E:\\信竞\\信竞文件夹\\in_c.TXT","r",stdin);
    cin>>n>>m>>st>>en;
    f(i,0,n)cin>>w2[i];
    memset(g,0x3f,sizeof g);
    f(i,0,m){
        int a,b,c;
        cin>>a>>b>>c;
        g[a][b]=g[b][a]=min(g[a][b],c);
    }
    dij();
    cout<<num[en]<<" "<<w[en]<<endl;
    pri(en);
}

Travel Plan

1030 Travel Plan (30 分)

思路:

和上一题差不多 , Dijkstra+更新各种信息

代码:

#include<bits/stdc++.h>
#define f(i,a,b) for(int i=a;i<b;i++)
#define ff(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=505;
int d[N],c[N],vis[N],pre[N];
int g1[N][N],g2[N][N];
int n,m,st,en;

void pri(int t){
	if(t==st){
		cout<<t<<" ";
		return ;
	}
	else{
		pri(pre[t]);
		cout<<t<<" ";
	}
}

void dij(){
	memset(d,0x3f,sizeof d);
	memset(c,0,sizeof c);
	d[st]=0;
	c[st]=0;
	f(i,0,n){
		int t=-1;
		f(j,0,n)
			if(!vis[j]&&(t==-1||d[t]>d[j]))t=j;
		vis[t]=1;
		f(j,0,n){
			if(d[j]>d[t]+g1[t][j]){
				d[j]=d[t]+g1[t][j];
				c[j]=c[t]+g2[t][j];
				pre[j]=t;
			}
			else if((d[j]==d[t]+g1[t][j]) && c[j]>c[t]+g2[t][j]){
				c[j]=c[t]+g2[t][j];
				pre[j]=t;
			}
		}
	}
}
int main(){
//	freopen("E:\\信竞\\信竞文件夹\\in_c.TXT","r",stdin);
	cin>>n>>m>>st>>en;
	memset(g1,0x3f,sizeof g1);
	memset(g2,0x3f,sizeof g2);
	
	f(i,0,m){
		int a,b,c,d;
		cin>>a>>b>>c>>d;
		g1[a][b]=g1[b][a]=min(g1[a][b],c);//dis
		g2[a][b]=g2[b][a]=min(g2[a][b],d);//cost
	}
	dij();
	pri(en);
	cout<<d[en]<<" "<<c[en];
}

Gas Station

1072 Gas Station (30 分)

题意:

从m个加油站里面选取1个站点,让他离居民区的最近的人最远,并且没有超出服务范围dist之内。
如果有很多个最远的加油站,输出距离所有居民区距离平均值最小的那个,如果平均值还是一样,就输出按照顺序排列加油站编号最小的那个

思路:

对n+m个点进行Dijkstra计算最短路径,记录相关信息即可

代码:

#include<bits/stdc++.h>
#define f(i,a,b) for(int i=a;i<b;i++)
#define ff(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=1e3+20;
const int inf=0x3f3f3f3f;
int n,m,k,ds;
int g[N][N],vis[N],d[N];
void dij(int p){
    memset(d,0x3f,sizeof d);
    memset(vis,0,sizeof vis);
    d[p]=0;
    f(i,0,n+m){
        int t=-1;
        //要对所有的点进行最短路(不止1~n)
        ff(j,1,n+m)
            if(!vis[j] && (t==-1||d[t]>d[j])) t=j;
        vis[t]=1;
        ff(j,1,n+m)
            d[j]=min(d[j],d[t]+g[t][j]);
    }
}
int f1(string s){
    int l=s.length();
    int t=0;
    if(s[0]=='G'){
        f(i, 1, l)
            t=t*10+(s[i]-'0');
        t+=n;//
    }
    else{
        f(i, 0, l)
            t=t*10+(s[i]-'0');
    }
    return t;
}
int main(){
    freopen("E:\\信竞\\信竞文件夹\\in_c.TXT","r",stdin);
    cin>>n>>m>>k>>ds;
    memset(g,0x3f,sizeof g);
    //防止有自环
    f(i,1,N)g[i][i]=0;
    f(i,0,k){
        string s1,s2;
        int t;
        cin>>s1>>s2>>t;
        int t1=f1(s1),t2=f1(s2);
        //防止有重边
        g[t1][t2]=g[t2][t1]=min(g[t1][t2],t);
    }
    double ind=-1,mx=-inf,ave=inf;
    ff(i,n+1,n+m){
        double t_ave=0,t_mn=inf;
        dij(i);
        ff(j,1,n){
            if(d[j]>ds){
                t_mn=-1;
                break;
            }
            //先找离居民区的最近的人
            if(d[j] < t_mn)
                t_mn=d[j];
            t_ave+=1.0*d[j]/n;
        }
        if(t_mn == -1)continue;
        if(t_mn > mx){
            mx=t_mn, ind= i - n, ave=t_ave;
        }
        else if(t_mn == mx && t_ave < ave){
            ind=i-n, ave=t_ave;
        }
    }
    if(ind==-1)cout<<"No Solution"<<endl;
    else printf("G%d\n%.1lf %.1lf", (int)ind, mx, ave);
}

也可以用substr函数和stoi函数函数来简单优化:

substr函数用法(截取指定位置,长度的字符串):

代码:

string s="abcdef";
cout<<s.substr(0,2)<<endl;
cout<<s.substr(1,2)<<endl;
cout<<s.substr(2,2)<<endl;

cout<<s.substr(0)<<endl;
cout<<s.substr(1)<<endl;
cout<<s.substr(2)<<endl;

输出:

ab
bc
cd
abcdef
bcdef
cdef

stoi函数用法(将 n n n 进制的字符串转化为10进制):

注意:蓝桥杯可能无法运行此类函数,不推荐在 OI 赛制的比赛里使用

代码:

//stoi(字符串,起始位置,n进制),将 n 进制的字符串转化为10进制
//将2进制的字符串 str 转换为10进制(从0位置开始,到其末尾)
string str = "1010";
int a = stoi(str, 0, 2);
cout << a << endl;

//默认是将字符串整体以10进制转换为10进制
str = "234";
cout << stoi(str) << endl;

输出:

10
234

代码:

{
    string s1,s2;
    int t,t1,t2;
    cin>>s1>>s2>>t;
    if(s1[0] == 'G') {
        s1 = s1.substr(1);
        t1 = n + stoi(s1);
    }
	else t1 = stoi(s1);

    if (s2[0] == 'G') {
        s2 = s2.substr(1);
        t2 = n + stoi(s2);
    }
	else t2 = stoi(s2);

    //防止有重边
    g[t1][t2]=g[t2][t1]=min(g[t1][t2],t);
}

All Roads Lead to Rome

1087 All Roads Lead to Rome (30 分)

思路:

统计信息,别乱就好 (写吐了)

代码:

#include<iostream>
#include<string>
#include<map>
using namespace std;
const int INF = 1e9;
const int N = 220;
int n, m,weight[N],g[N][N],d[N],w[N];
int pre[N], pt[N], num[N];
bool vis[N] ;
string st;
map <string, int> cityToID;
map<int, string> idToCity;
bool findID(string a) {
    if (cityToID.find(a) != cityToID.end())	return true;
    return false;
}
void Dijkstra(int s) {
    fill(d, d + N, INF);
    fill(pt, pt + N, 0);
    fill(w, w + N, 0);
    fill(num, num + N, 0);
    d[s] = 0;
    w[s] = weight[s];
    num[s] = 1;
    for (int i = 0; i < n; ++i) {
        int u = -1, mn = INF;
        for (int j = 0; j < n; ++j) {
            if (!vis[j] && d[j] < mn) {
                u = j;
                mn = d[j];
            }
        }
        //找不到小于INF的d[u] 说明剩下的顶点和起点a不连通
        if (u == -1)return;
        vis[u] = true; //别忘了标记
        //用u去更新其他点
        for (int v = 0; v < n; ++v) {
            if (!vis[v] && g[u][v] != INF) {
                if (d[u] + g[u][v] < d[v]) {
                    //st->v 变为 st->u->v
                    d[v] = d[u] + g[u][v];//最小花费
                    w[v] = w[u] + weight[v];//从起点到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]) {//看幸福值
                        w[v] = w[u] + weight[v];
                        pt[v] = pt[u] + 1;
                        pre[v] = u;
                    }
                }
                else if (w[u] + weight[v] == w[v]) {//幸福值同
                    double uAvg = 1.0 * (w[u] + weight[v]) / (pt[u] + 1);
                    double vAvg = 1.0 * w[v] / pt[v];
                    if (uAvg > vAvg) {//看平均幸福值
                        pt[v] = pt[u] + 1;
                        pre[v] = u;
                    }
                }
            }
        }
    }

}
void printPath(int v) {
    if (v == 0) {
        cout << idToCity[v];
        return;
    }
    printPath(pre[v]);
    cout << "->" << idToCity[v];
}
int main(void) {
    cin >> n >> m >> st;
    string t;
    int id = 0,numT;
    cityToID[st] = id++;
    idToCity[id - 1] = st;
    for (int i = 0; i < n - 1; ++i) {
        cin >> t >> numT;
        cityToID[t] = id++;
        idToCity[id - 1] = t;
        weight[id - 1] = numT;
    }
    string a, b;
    int dis = 0;
    fill(g[0], g[0] + N * N, INF);
    for (int i = 0; i < m; ++i) {
        cin >> a >> b >> dis;
        int x=cityToID[a],y=cityToID[b];
        g[x][y] = dis;
        g[y][x] = dis;
    }
    Dijkstra(0);
    int rom = cityToID["ROM"];
    printf("%d %d %d %d\n", num[rom], d[rom], w[rom], w[rom] / pt[rom]);
    printPath(rom);
    return 0;
}

Public Bike Management

1018 Public Bike Management (30 分)

思路:

只有在所有路径都确定了之后才能区选择最小的need和最小的back,dfs就行

代码:

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
const int N = 510;	//最大顶点数
const int INF = 1e9;	//无穷大
//n为顶点数,m为边数,Cmax为最大容量.Sp为问题站点
//G为邻接矩阵,weight为点权,minReamin记录最少带回的数目
int n, m, Cmax, Sp, numPath = 0, g[N][N], weight[N],d[N];
int vis[N] ;
vector<int> pre[N];	//前驱
vector<int> t_Path, path;	//临时路径及最优路径
int minNeed = INF, minRemain = INF;
void Dijkstra(int s) {
    fill(d, d + N, 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] && d[j] < MIN) {
                u = j;
                MIN = d[j];
            }
        }
        //找不到小于INF的d[u],说明剩下的顶点和起点s不连通.
        if (u == -1)	return;
        vis[u] = true;	//标记u为已访问
        for (int v = 0; v <= n; ++v) {
            //如果v未访问u能到达v
            if (!vis[v] && 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);
            }
        }
    }
}
void DFS(int v) {
    if (v == 0) {
        t_Path.push_back(v);
        //路径tempPath上需要携带的数目,需要带回的数目
        int need = 0, remain = 0;
        for (int i = t_Path.size() - 1; i >= 0; --i) {//必须要倒着枚举
            int id = t_Path[i];//当前结点编号为id
            if (weight[id] > 0)	remain += weight[id];
            else {
                if (remain > abs(weight[id]))	remain -= abs(weight[id]);
                else {	//当前有的不够补给
                    need += abs(weight[id])-remain;
                    remain = 0;
                }
            }
        }
        if (need < minNeed) {	//比较一下,那个需要的自行车少.
            minNeed = need;		//优化minNeed
            minRemain = remain;	//优化minRemain
            path = t_Path;
        }
        else if (need == minNeed && remain < minRemain) {
            //需要的自行车相同的.reamin带会来的变少了.
            minRemain = remain;	//优化需要带回的自行车
            path = t_Path;
        }
        t_Path.pop_back();
        return;
    }
    t_Path.push_back(v);
    for (int i = 0; i < pre[v].size(); ++i)	DFS(pre[v][i]);
    t_Path.pop_back();
}
int main(void) {
    scanf("%d%d%d%d", &Cmax, &n, &Sp, &m);
    int u, v;
    fill(g[0], g[0] + N * N, INF);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &weight[i]);
        weight[i] -= Cmax / 2;
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d%d", &u, &v);
        scanf("%d", &g[u][v]);
        g[v][u] = g[u][v];
    }
    Dijkstra(0);
    DFS(Sp);
    printf("%d ", minNeed);
    for (int i = path.size() - 1; i >= 0; --i) {
        printf("%d", path[i]);
        if (i > 0)	printf("->");
    }
    printf(" %d", minRemain);
    return 0;
}

直捣黄龙

L3-011 直捣黄龙 (30 分)

代码:

#include<bits/stdc++.h>
#define f(i,a,b) for(int i=a;i<b;i++)
#define ff(i,a,b) for(int i=a;i<=b;i++)
#define debug(x) cout<<#x<<" : "<<x<<endl;
using namespace std;
//
const int N=205;
int g[N][N],w[N],st[N];
int n,m,cnt,t_1,t_2;
string s_1,s_2;
map<string,int> mp;
map<int,string> mp2;
//时间,路径数目,经过点数,杀敌数目,前驱结点
int d[N],num[N],sum[N],kl[N],pre[N];

void pri(int t){
    if(t!=t_1){
        pri(pre[t]);
        cout<<"->"<<mp2[t];
    }
    else cout<<mp2[t];
}

void dij(){
    memset(d,0x3f,sizeof d);
    d[t_1]=0,num[t_1]=1;//路径初始化为1
    f(k,0,n){
        int t=-1;
        f(i,0,n){
            if(!st[i] && (t==-1||d[t]>d[i]))t=i;
        }
        st[t]=1;
        f(i,0,n){
            if(d[i]>d[t]+g[t][i]){
                d[i]=d[t]+g[t][i];
                num[i]=num[t];
                sum[i]=sum[t]+1;
                kl[i]=kl[t]+w[i];
                pre[i]=t;
            }
            else if(d[i]==d[t]+g[t][i]){
                num[i]+=num[t];
                if(sum[i]<sum[t]+1){
                    sum[i]=sum[t]+1;
                    kl[i]=kl[t]+w[i];
                    pre[i]=t;
                }
                else if(sum[i]==sum[t]+1 && kl[i]<kl[t]+w[i]){
                    kl[i]=kl[t]+w[i];
                    pre[i]=t;
                }
            }
        }
    }
}

int main(){
    cin>>n>>m>>s_1>>s_2;
    mp[s_1]=cnt;mp2[cnt]=s_1;cnt++;
    f(i,0,n-1){
        string s;int t;
        cin>>s>>t;
        mp[s]=cnt;mp2[cnt]=s;w[cnt]=t;cnt++;
    }
    memset(g,0x3f,sizeof g);
    f(i,0,m){
        string s1,s2;int t;
        cin>>s1>>s2>>t;
        int x=mp[s1],y=mp[s2];
        g[x][y]=g[y][x]=min(g[x][y],t);
    }
    t_1=mp[s_1],t_2=mp[s_2];
    dij();
    pri(t_2);
    cout<<endl;
    cout<<num[t_2]<<" "<<d[t_2]<<" "<<kl[t_2]<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值