过路费
〖题目描述〗
约翰家有N 片草地,编号为1 到N,彼此之间由M 条双向道路连接,第i 条
道路连接草地Ai 和Bj,两片之间可能有多条道路,但没有道路会连接同一片草
地,现有的道路可以保证任意两片草地之间都是连通的。
约翰宣布,从即日开始,为了“低碳节能”,每条道路都要收到过路费,第
i 路收费Li 元。此外,约翰还要求每头奶牛购买牌照,他为每片草地设置了牌
照标准,如果奶牛购买的牌照价格低于某片草地的标准,她将被禁止进入那片草
地。第i 片草地的牌照标准为Ci。
奶牛们敢怒不敢言,有K 头奶牛跑来向你咨询省钱的办法,第i 头奶牛要从
草地Si 走到Ti。请你帮她们算算,选择什么样的路线才能最省钱?
〖输入〗
第一行:三个整数N,M 和K,1≤N≤250,1≤M≤10000,1≤K≤10000
第二行到N+1 行:第i+1 行有一个整数:Ci,1≤Ci≤106
第N+2 行到N+M+1 行:第i+N+1 行有三个整数Ai,Bi 和Li,1≤Ai≤N,1≤Bi≤
N,1≤Li≤106
第N+M+2 行到N+M+K+1 行:第i+N+M+1 行有两个整数Si 和Ti,1≤Si≤N,1≤
Ti≤N
〖输出〗
第一行到K 行:第i 行表示Si 到Ti 的最低费用是多少
〖样例输入〗
5 7 2
2
5
3
3
4
1 2 3
1 3 2
2 5 3
5 3 1
5 4 1
2 4 3
3 4 4
1 4
2 3
〖样例输出〗
8
9
〖样例说明〗
从1 到4 的最好办法是1→3→5→4,从2 到3 的最好办法是2→5→3
方法一:暴力O(n^4 ~ n^2*m)
预计得分40’~60’
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<queue>
#define N 300
#define M 20010
using namespace std;
const int inf=1<<30;
int n,m,k,c[N],c1[N],pmt[N],tot=0,vis[N],dist[N];
struct Edge{
int v,l;
};
vector <Edge>e[N];
int ans[N][N];
void init(){
scanf("%d%d%d",&n,&m,&k);
for (int i=1;i<=n;i++) scanf("%d",&c[i]);
for (int i=1;i<=n;i++) c1[i]=c[i];
sort(c1+1,c1+n+1);
pmt[++tot]=c1[1];
for (int i=2;i<=n;i++)
if (c1[i]!=c1[i-1]) pmt[++tot]=c1[i];
int a,b,w;
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&w);
e[a].push_back((Edge){b,w});
e[b].push_back((Edge){a,w});
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++) ans[i][j]=inf;
}
void spfa(int s,int w){//认为w为牌照,做最短路径
memset(vis,0,sizeof vis);
for (int i=1;i<=n;i++) dist[i]=inf;
vis[s]=1;dist[s]=0;
queue <int> q;
q.push(s);
while (!q.empty())
{
int u=q.front();q.pop();
for (int i=0;i<e[u].size();i++)
{
int v=e[u][i].v;
if (c[v]>w) continue;//进行限制
if (dist[u]+e[u][i].l < dist[v])
{
dist[v] = dist[u]+e[u][i].l;
if (!vis[v])
{
vis[v]=1;
q.push(v);
}
}
vis[u]=0;
}
}
for (int i=1;i<=n;i++)
if (c[i]<=w) ans[s][i]=min(ans[s][i],dist[i]+w);
}
int main(){
freopen("toll.in","r",stdin);
freopen("toll.out","w",stdout);
init();
for (int s=1;s<=n;s++)
for (int i=1;i<=tot;i++)
if (pmt[i]>=c[s]) spfa(s,pmt[i]);//出现过的牌照费
int s,t;
while(k--)
{
scanf("%d%d",&s,&t);
printf("%d\n",ans[s][t]);
}
return 0;
}
然而暴力不是全部,要动脑。
进行排序,我们可以得到:1、路径长度是严格不降的;
2、当前i->j的费用=路程+路程上的最大点权。
由于我们是对升序枚举中间点,可以保证最短路持续更新总费用。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<queue>
#define N 100
using namespace std;
const int inf=1<<30;
struct data{
int w,id;
}c[N];
int n,m,T,d[N][N],w[N][N],ini[N];
int ans=0;
bool cmp(data a,data b){return a.w<b.w;}
void init(){
scanf("%d %d %d\n",&n,&m,&T);
for (int i=1;i<=n;i++) scanf("%d",&c[i].w),c[i].id=i;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++) d[i][j]=w[i][j]=inf;
int a,b,v;
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&v);
d[a][b]=d[b][a]=min(d[a][b],v);
d[i][i]=0;
w[i][i]=c[i].w;
}
sort(c+1,c+n+1,cmp);
for (int i=1;i<=n;i++) ini[c[i].id]=c[i].w;//还原排序前的c数组
}
void floyd(){
for (int id=1;id<=n;id++)
{
int k=c[id].id;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
d[i][j]=d[j][i]=min(d[i][j],d[i][k]+d[k][j]);
w[i][j]=w[j][i]=min(w[i][j],max(c[id].w,max(ini[i],ini[j]))+d[i][j]);
}
}
}
int main(){
freopen("toll.in","r",stdin);
freopen("toll.out","w",stdout);
init();
floyd();
int s,t;
while(T--)
{
scanf("%d%d",&s,&t);
printf("%d\n",w[s][t]);
}
return 0;
}