1. 程式人生 > >NOIP-模擬試題之--過路費

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