NOIP-模拟试题之--过路费

2018 NOIP 全套资料下载

【问题描述】

为了发财,Mr_he在他的农场设置了一系列的规章制度,使得任何一只奶牛在农场中的道路行走,都要向他交过路费。
  农场中有 n 片草地(编号为1~n),并且有 m 条双向道路把这些草地连接起来。Mr_he已经在每条双向道路上设置一个过路费wi。可能有多条道路连接相同的两片草地,但是不存在一条道路连接一片草地和这片草地本身。最值得庆幸的是,奶牛从任意一片草地出发,经过一系列的路径,总是可以抵达其他任意一片草地。
  为了最大化自己的收入。Mr_he在每片草地上也设置了一个过路费用ci。并规定,奶牛从一片草地到另一片草地需要交纳的费用,是经过的所有道路的费用之和,加上经过的所有草地(包括起点和终点)的过路费的最大值。
  任劳任怨的牛们希望去调查一下,它们每一次的出行应该选择那条路径才能最节俭。她们要你写一个程序,接受 k 个问题并且输出每个询问对应的最小花费。第 i 个问题包含两个数字 s,t,表示起点和终点草地。

【输入格式】

第 1 行:三个用空格隔开的整数:n,m,k。
  第 2 到第 n+1 行:第 i+1 行包含一个单独的整数 c(1<=c<=100000),表示第 i 片草地的费用。
  第 n+2 行到第 n+m+1 行:第 j+n+1 行包含 3 个用空格隔开的整数:a,b,w(1<=a,b<=n,1<=w<=100000),表示一条道路(a,b)的费用为 w 。
  第 n+m+2 到第 n+m+k+1 行:第 i+n+m+1 行表示第 i 个问题,包含两个用空格隔开的整数 s 和 t(1<=s,t<=n 且 s!=t),表示一条询问的起点和终点。

【输出格式】

第 1 到第 k 行:第 i 行包含一个整数,表示从 s 到 t 的最小花费。

【输入样例】

5 7 3
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 3
1 4
2 3

【输出样例】

5
8
9

【样例解释】
。。不给!!!!(滑稽)

————————————————————————————————————————————————————
根据题目交代的路径费用的定义,可以想到枚举每一个点作为权值最大点的情况。这样就可以保证算出来的结果是正确的了(移动到这个最大点肯定是要用最短路啦)。但是显然每一组询问都去算的话是会超时的。面对10000组询问,可以想到打表。那么在知道这个点是最大值路径上的最大值并求出单源最短路之后怎么办呢?显然对于任意一条以点k为最大点值的点的路径i->j,可以拆分成i->k和k->j,那么就是说在这种情况下我们可以更新i->j的最小费用路径了。时间复杂度O(N*MlogN+N^3) ,已经可以AC,而且由于有剪枝条件的存在一般情况下达不到这个时间。

下面是dijkstra的代码:

#include< iostream>
#include< cstdio>
#include< cstring>
#include< cstdlib>
#include< algorithm>
#include< cmath>
#include< queue>
#include< set>
#include< map>
#include< vector>
#include< cctype>
#define inf 1e8+5
using namespace std;
const int maxn=255;
const int maxm=10005;

int N,M,K,C[maxn];
int dist[maxn][maxn];
struct edge{ int to,next,w; }E[maxm<<1];
int first[maxn],np;
int f[maxn],_f[maxn];
struct state{ int id,v; };
struct cmp{
bool operator () (state a,state b)
{
return a.v>b.v;
}
};

void _scanf(int &x)
{
x=0;
char ch=getchar();
while(ch<‘0’||ch>‘9’) ch=getchar();
while(ch>=‘0’&&ch<=‘9’) x=x*10+ch-‘0’,ch=getchar();
}
void add_edge(int u,int v,int w)
{
E[++np]=(edge){v,first[u],w};
first[u]=np;
}
void data_in()
{
_scanf(N);_scanf(M);_scanf(K);
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
dist[i][j]=inf;
for(int i=1;i<=N;i++) _scanf(C[i]);
int x,y,z;
for(int i=1;i<=M;i++)
{
_scanf(x);_scanf(y);_scanf(z);
add_edge(x,y,z);
add_edge(y,x,z);
}
}
void dijkstra(int s)
{
for(int i=1;i<=N;i++) f[i]=inf;
priority_queue<state,vector,cmp>pq;
pq.push((state){s,0});
f[s]=0;
state tmp;
int i,j;
while(!pq.empty())
{
tmp=pq.top();pq.pop();
i=tmp.id;
if(f[i]<tmp.v) continue;
for(int p=first[i];p;p=E[p].next)
{
j=E[p].to;
if(C[j]>C[s]) continue;
if(f[i]+E[p].w<f[j])
{
f[j]=f[i]+E[p].w;
pq.push((state){j,f[j]});
}
}
}
}
void work()
{
for(int i=1;i<=N;i++)
{
dijkstra(i);
for(int j=1;j<=N;j++)
for(int k=1;k<=N;k++)
dist[j][k]=min(dist[j][k],f[j]+f[k]+C[i]);
}
int x,y;
for(int i=1;i<=K;i++)
{
_scanf(x);_scanf(y);
printf("%d\n",dist[x][y]);
}
}
int main()
{
freopen(“roadtoll.in”,“r”,stdin);
freopen(“roadtoll.out”,“w”,stdout);
data_in();
work();
return 0;
}

把所有的点按照权值由小到大排序之后就得到了floyd的算法了。注意到算最大权值的时候floyd里面要用到两个端点的权值来决策。

下面是floyd的代码:

#include< iostream>
#include< cstdio>
#include< cstring>
#include< cstdlib>
#include< algorithm>
#include< cmath>
#include< queue>
#include< set>
#include< map>
#include< vector>
#include< cctype>
#define inf 1e8+5
using namespace std;
const int maxn=255;

int N,M,K,C[maxn];
int g[maxn][maxn],dist[maxn][maxn];
struct data{ int id,v; }p[maxn];

void _scanf(int &x)
{
x=0;
char ch=getchar();
while(ch<‘0’||ch>‘9’) ch=getchar();
while(ch>=‘0’&&ch<=‘9’) x=x*10+ch-‘0’,ch=getchar();
}
void data_in()
{
_scanf(N);_scanf(M);_scanf(K);
for(int i=1;i<=N;i++) _scanf(C[i]);
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
if(i!=j) g[i][j]=inf;
int x,y,z,t;
for(int i=1;i<=M;i++)
{
_scanf(x);_scanf(y);_scanf(z);
t=min(g[x][y],z);
g[x][y]=g[y][x]=t;
}
}
bool cmp(data x,data y) { return x.v<y.v; }
void floyd()
{
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
if(i!=j) dist[i][j]=inf;
for(int k=1;k<=N;k++)
{
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
if(g[i][p[k].id]+g[p[k].id][j]<g[i][j])
g[i][j]=g[i][p[k].id]+g[p[k].id][j];
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
{
int maxc=max(p[k].v,max(C[i],C[j]));
if(g[i][p[k].id]+g[p[k].id][j]+maxc<dist[i][j])
dist[i][j]=g[i][p[k].id]+g[p[k].id][j]+maxc;
}
}
}
void work()
{
for(int i=1;i<=N;i++)
p[i]=(data){i,C[i]};
sort(p+1,p+N+1,cmp);
floyd();
int x,y;
for(int i=1;i<=K;i++)
{
_scanf(x);_scanf(y);
printf("%d\n",dist[x][y]);
}
}
int main()
{
freopen(“roadtoll.in”,“r”,stdin);
freopen(“roadtoll.out”,“w”,stdout);
data_in();
work();
return 0;
}
然后还有一个变化的版本,把上面的最大权值改成次大,怎么办呢?
floyd就失效了ORZ(反正我是没有写出来的),只能dijkstra打表。第一步还是算单源情况下某个点为大点的最短路,过程中把C[j]>=C[s]的点(s是起点)放进一个队列,然后用这些元素作为起始值来求最短路(不想写在一起)。

#include< iostream>
#include< cstdio>
#include< cstring>
#include< cstdlib>
#include< algorithm>
#include< cmath>
#include< queue>
#include< set>
#include< map>
#include< vector>
#include< cctype>
#define inf 1e8+5
using namespace std;
const int maxn=255;
const int maxm=10005;

int N,M,K,C[maxn];
int dist[maxn][maxn];
struct edge{ int to,next,w; }E[maxm<<1];
int first[maxn],np;
int f[maxn],_f[maxn];
struct state{ int id,v; };
struct cmp{
bool operator () (state a,state b)
{
return a.v>b.v;
}
};
queueq;

void _scanf(int &x)
{
x=0;
char ch=getchar();
while(ch<‘0’||ch>‘9’) ch=getchar();
while(ch>=‘0’&&ch<=‘9’) x=x*10+ch-‘0’,ch=getchar();
}
void add_edge(int u,int v,int w)
{
E[++np]=(edge){v,first[u],w};
first[u]=np;
}
void data_in()
{
_scanf(N);_scanf(M);_scanf(K);
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
dist[i][j]=inf;
for(int i=1;i<=N;i++) _scanf(C[i]);
int x,y,z;
for(int i=1;i<=M;i++)
{
_scanf(x);_scanf(y);_scanf(z);
add_edge(x,y,z);
add_edge(y,x,z);
}
}
void dijkstra(int s)
{
for(int i=1;i<=N;i++) f[i]=inf;
priority_queue<state,vector,cmp>pq;
pq.push((state){s,0});
f[s]=0;
state tmp;
int i,j;
while(!pq.empty())
{
tmp=pq.top();pq.pop();
i=tmp.id;
if(f[i]<tmp.v) continue;
for(int p=first[i];p;p=E[p].next)
{
j=E[p].to;
if(C[j]>=C[s]) q.push((state){j,f[i]+E[p].w});
if(C[j]>C[s]) continue;
if(f[i]+E[p].w<f[j])
{
f[j]=f[i]+E[p].w;
pq.push((state){j,f[j]});
}
}
}
}
void _dijkstra(int s)
{
for(int i=1;i<=N;i++) _f[i]=inf;
priority_queue<state,vector,cmp>pq;
state tmp;
int i,j;
while(!q.empty())
{
tmp=q.front();q.pop();
i=tmp.id;
if(_f[i]<tmp.v) continue;
_f[i]=tmp.v;
pq.push((state){i,_f[i]});
}
while(!pq.empty())
{
tmp=pq.top();pq.pop();
i=tmp.id;
if(_f[i]<tmp.v) continue;
for(int p=first[i];p;p=E[p].next)
{
j=E[p].to;
if(C[j]>C[s]) continue;
if(_f[i]+E[p].w<_f[j])
{
_f[j]=_f[i]+E[p].w;
pq.push((state){j,_f[j]});
}
}
}
}
void work()
{
for(int i=1;i<=N;i++)
{
dijkstra(i);
_dijkstra(i);
for(int j=1;j<=N;j++)
for(int k=1;k<=N;k++)
dist[j][k]=min(dist[j][k],min(f[j]+_f[k],_f[j]+f[k])+C[i]);
}
int x,y;
for(int i=1;i<=K;i++)
{
_scanf(x);_scanf(y);
printf("%d\n",dist[x][y]);
}
}
int main()
{
freopen(“test.in”,“r”,stdin);
freopen(“test.out”,“w”,stdout);
data_in();
work();
return 0;
}


原文:https://blog.csdn.net/qq_39439314/article/details/78174152

猜你喜欢

转载自blog.csdn.net/tianli315/article/details/84965494