NOIP复习模拟赛day6

前言:

明天就要去福州了......

还有今天第一题好难啊......(五中niubi)

正题:

3: 野外拓展训练

Description

Nicloe所在的公司经常组织职员进行一些拓展训练。上个季度刚刚举行了一次定向运动,大家反映很好。这个月轮到Nicloe的部门做策划,既然大家都已经熟悉了看地图和使用指南针,她们决定在森林公园做一次与定向运动类似的野外拓展训练,不过规则稍作修改。

由于大家不是专业运动员,体力有限,现在Nicloe准备通过计算机的帮助寻找各种情况下的最短路线,以便从中选出部分作为本次拓展训练的方案,请你帮忙设计程序。

制定的规则如下:地图上包括起点与终点在内共N个点,每两点之间有一条单行道,共有M条;从所有点中选择其中K个作为一个方案的有效点标,允许大家从规定的起点按任意顺序经过所有有效点标到达规定的终点,求出最短距离。

Input Format

第一行5个整数n、m、k、s、t,分别表示地图上已有点的总个数、每两点间单行道总条数、本次有效点标个数、起点编号、终点编号。

接下来m行,每行3个整数x、y、z,表示有一条从x点到y点的长度为z的单行道。 接下来k行,每行1个整数,表示本次有效点标的编号。

Output Format

输出一个整数,表示最短距离,若没有方案可行则输出-1。

Sample Input

【输入样例1】
3 3 2 1 1
1 2 1
2 3 1
3 1 1
2
3
【输入样例2】
4 6 2 1 1
1 2 1
2 3 1
3 4 1
4 1 1
3 1 1
4 2 1
3
4

Sample Output

【输出样例1】
3
【样例1解释】
路径为1->2->3->1。
【输出样例2】
4

【样例2解释】
路径为1->2->3->4->1。

Hint

【数据范围】 20%的数据n≤10。

50%的数据n≤1000。

100%的数据n≤50000,m≤100000,0≤k≤10,1≤z≤5000。

另有20%的数据k=0。

题解:

我们可以考虑先做K遍的SPFA预处理出K个有效点标到各个地方的距离

预处理后又有什么用呢?

我们考虑起点S(S不是有效点标),对于S来说,肯定是走到这K个有效点标中的某一个,然后走到其他几个有效点标(可能经过自己),最后再从某个有效点标到达终点。

所以呢,我们可以考虑再预处理一个数组f[i][j],表示走完这K个有效点标,起点为第i个有效点标,终点为第j个有效点标的最短路径(可能经过我们的u)。

这样我们可以通过dfs预处理出f数组。

那么统计答案的时候我们只需要枚举个这K个有效点标中的起点和终点就可以了。

统计答案:ans=min(ans,dis[p][i]+dis[q][i]+f[p][q])

到此,这题就被我们暴力过掉了。

 1 //NOIPRP++
 2 #include<bits/stdc++.h>
 3 #define Re register int
 4 using namespace std;
 5 int N,M,K,dis[11][50005],vis[50005],k[11],head[50005],Num,ans=INT_MAX,f[11][11],St,u,v,w,S,T;
 6 queue < int > q;
 7 struct Edge{
 8     int Next,To,w;
 9 }edge[100005];
10 inline void read(int &x){
11     x=0;char c=getchar();bool p=1;
12     for (;'0'>c||c>'9';c=getchar()) if (c=='-') p=0;
13     for (;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
14     p?:x=-x;
15 }
16 inline void SPFA(int S,int *dist){
17     for (Re i=1; i<=N; i++) dist[i]=0x3FFFFFFF,vis[i]=0;
18     q.push(S); dist[S]=0; vis[S]=1;
19     while (!q.empty()){
20         int Now=q.front(); q.pop(); vis[Now]=0;
21         for (Re i=head[Now];i;i=edge[i].Next)
22             if (dist[edge[i].To]>dist[Now]+edge[i].w){
23                 dist[edge[i].To]=dist[Now]+edge[i].w;
24                 if (!vis[edge[i].To]) vis[edge[i].To]=1,q.push(edge[i].To);
25             }
26     } return;
27 }
28 inline void dfs(int Now,int Deep,int Dis){
29     if (Deep==K){
30         f[St][Now]=min(f[St][Now],Dis);
31         return;
32     }
33     vis[Now]=1;
34     for (Re i=1;i<=K;i++) if (!vis[i]) dfs(i,Deep+1,Dis+dis[Now][k[i]]);
35     vis[Now]=0; return;
36 }
37 int main(){
38     Re i,j,t;
39     read(N);read(M);read(K);read(S);read(T);
40     for (i=1;i<=M;i++){
41         read(u);read(v);read(w);
42         edge[++Num].Next=head[u];edge[Num].To=v;edge[Num].w=w;head[u]=Num;
43     }
44     for (i=1;i<=K;i++) read(k[i]); k[0]=S;
45     for (i=0;i<=K;i++) SPFA(k[i],dis[i]);
46     if (K==0){
47         if (dis[0][T]==1061109567) printf("-1"); else printf("%d",dis[0][T]);
48         return 0;
49     }
50     memset(f,63,sizeof(f));memset(vis,0,sizeof(vis));    
51     for (i=1;i<=K;i++){
52         St=i;dfs(St,1,0);
53     }
54     for (i=1;i<=K;i++) vis[k[i]]=1;
55     for (i=1;i<=K;i++)
56     for (j=1;j<=K;j++){
57         if (i==j) continue;
58         ans=min(ans,dis[0][k[i]]+dis[j][T]+f[i][j]);
59     }
60     if (ans>=1061109567) printf("-1");
61     else printf("%d",ans);
62     return 0;
63 }
64 //NOIPRP++
View Code
原文地址:https://www.cnblogs.com/to-the-end/p/9929157.html