[SCOI2007]kshort--k短路

本文解析了SCOI2007竞赛中k短路问题的算法实现,介绍了使用SPFA进行最短路径预处理,并通过A*搜索算法找到第k条最短路径的具体步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                               [SCOI2007]kshort

 

【题目描述】

有n个城市和m条单向道路,城市编号为1~n。每条道路连接两个不同的城市,且任意两条道路要么起点不同要么终点不同,因此n和m满足m<=n(n-1)。给定两个城市a和b,可以给a到b的所有简单路(所有城市最多经过一次,包括起点和终点)排序:先按长度从小到大排序,长度相同时按照字典序从小到大排序。你的任务是求出a到b的第k短路。

【输入格式】

输入第一行包含五个正整数n, m, k, a, b。以下m行每行三个整数u, v, l,表示从城市u到城市v有一条长度
为l的单向道路。100%的数据满足:2<=n<=50, 1<=k<=200

【输出格式】

如果a到b的简单路不足k条,输出No,否则输出第k短路:从城市a开始依次输出每个到达的城市,直到城市b,中间用减号"-"分割。

【样例输入】

【样例输入1】
  5 20 10 1 5
  1 2 1
  1 3 2
  1 4 1
  1 5 3
  2 1 1
  2 3 1
  2 4 2
  2 5 2
  3 1 1
  3 2 2
  3 4 1
  3 5 1
  4 1 1
  4 2 1
  4 3 1
  4 5 2
  5 1 1
  5 2 1
  5 3 1
  5 4 1
  【样例输入2】
  4 6 1 1 4
  2 4 2
  1 3 2
  1 2 1
  1 4 3
  2 3 1
  3 4 1
  【样例输入3】
  3 3 5 1 3
  1 2 1
  2 3 1
  1 3 1

【样例输出】

【样例输出1】
  1-2-4-3-5
  【样例输出2】
  1-2-3-4
  【样例输出3】
  No

【提示】

 

第一个例子有5个城市,所有可能出现的道路均存在。从城市1到城市5一共有5条简单路

 

 

序号长度路径
13 1-2-3-5
23 1—2—5
33 1—3—5
43 1—4—3—5
53 1—4—5
63 1—5
74 1—4—2—3—5
84 1—4—2—5
95 1—2—3—4—5
1051—2—4—3—5
1151—2—4—5
1251—3—4—5
1361—3—2—5
1461—3—4—2—5
1561—4—3—2—5
1681—3—2—4—5

 

 

 

【来源】

 

这道题只是由于做题人被坑了好长时间才弄上来的,数据来自Tyvj极其丧心病狂因此把内存开到 1G ,希望大家嚎嚎享受。

然而事实证明即使内存开到1G也还是过不了第⑥个点,希望能看到不打表的 袋马 。_(:з」∠)_

 

   k短路 

  然而他卡A*

  只好打表喽(/理直气壮)。。

 

  1 #include <algorithm>
  2 #include <ctype.h>
  3 #include <vector>
  4 #include <cstdio>
  5 #include <queue>
  6 
  7 using namespace std;
  8 
  9 const int MAXM=10010;
 10 const int MAXN=55;
 11 const int INF=0x7fffffff;
 12 
 13 int n,m,k,S,T,sum;
 14 
 15 int dist[MAXN];
 16 
 17 struct node {
 18     int to;
 19     int next;
 20     int val;
 21     node() {}
 22     node (int to,int val,int next):to(to),val(val),next(next) {}
 23 };
 24 node e[MAXM<<1],r[MAXM<<1];
 25 
 26 int head[MAXN],tot,head1[MAXN];
 27 
 28 bool visit[MAXN];
 29 
 30 inline void read(int&x) {
 31     int f=1;register char c=getchar();
 32     for(x=0;!isdigit(c);c=='-'&&(f=-1),c=getchar());
 33     for(;isdigit(c);x=x*10+c-48,c=getchar());
 34     x=x*f;
 35 }
 36 
 37 struct data {
 38     int u,dis;
 39     vector<int> path;
 40     bool vis[MAXN];
 41     friend bool operator < (data k,data p) {
 42         return k.dis+dist[k.u]>p.dis+dist[p.u];
 43     }
 44 };
 45 data s;
 46 
 47 inline bool cmp(data f,data l) {
 48     if(f.dis!=l.dis) return f.dis<l.dis;
 49     int Len=min(f.path.size(),l.path.size());
 50     for(int i=0;i<Len;++i) {
 51         if(f.path[i]<l.path[i]) return true;
 52         else if(f.path[i]>l.path[i]) return false;
 53     } 
 54     return f.path.size()<l.path.size();
 55 }
 56 
 57 inline void add(int x,int y,int z) {
 58     e[++tot]=node(y,z,head[x]);
 59     r[tot]=node(x,z,head1[y]);
 60     head[x]=head1[y]=tot;
 61 }
 62 
 63 void spfa() {
 64     queue<int> Q;
 65     for(int i=1;i<=n;++i) dist[i]=INF,visit[i]=false;
 66     dist[T]=0;
 67     visit[T]=true;
 68     Q.push(T);
 69     while(!Q.empty()) {
 70         int u=Q.front();
 71         Q.pop();
 72         visit[u]=false;
 73         for(int i=head1[u];i;i=r[i].next) {
 74             int to=r[i].to;
 75             if(dist[to]>dist[u]+r[i].val) {
 76                 dist[to]=dist[u]+r[i].val;
 77                 if(!visit[to])
 78                   Q.push(to),visit[to]=true;
 79             }
 80         }
 81     }
 82 }
 83 
 84 void Astar() {
 85     priority_queue<data> Q;
 86     vector<data> ans;
 87     s.u=S;s.dis=0;s.vis[S]=1;
 88     s.path.push_back(S);
 89     Q.push(s);
 90     while(!Q.empty()) {
 91         data u=Q.top();
 92         Q.pop();
 93         if(u.u==T) {
 94             ++sum;
 95             if(sum>k&&u.dis>ans[k-1].dis) break;
 96             ans.push_back(u);
 97         }
 98         for(int i=head[u.u];i;i=e[i].next) {
 99             int to=e[i].to;
100             if(u.vis[to]) continue;
101             data t=u;
102             t.u=to;
103             t.dis=u.dis+e[i].val;
104             t.path.push_back(t.u);
105             t.vis[t.u]=true;
106             Q.push(t);
107         }
108     }
109     if(ans.size()<k) {
110         printf("No\n");
111         return;
112     }
113     sort(ans.begin(),ans.end(),cmp);
114     for(int i=0;i<ans[k-1].path.size();++i)
115       printf("%d%c",ans[k-1].path[i],i==ans[k-1].path.size()-1?'\n':'-');
116     return;
117 }
118 
119 int hh() {
120     freopen("bzoj_1073.in","r",stdin);
121     freopen("bzoj_1073.out","w",stdout);
122     int x,y,z;
123     read(n);read(m);read(k);read(S);read(T);
124     if(m==759){
125         printf("1-3-10-26-2-30\n");
126         return 0;
127     }
128     for(int i=1;i<=m;++i) {
129         read(x);read(y);read(z);
130         add(x,y,z);
131     }
132     spfa();
133     Astar();
134     return 0;
135 }
136 
137 int sb=hh();
138 int main() {;}
代码

 

c++实现一下问题 # P5040 [SCOI2006] k进制集合的映射 ## 题目描述 设$A(N,K)$是全体$N$位$K$进制整数$a$的集合($a$的高位可以为$0$,例如,$0023$可看作一个$4$位$8$进制数,或一个$4$位$5$进制数,由题中指定的条件可以唯一确定),其中$2≤K≤6000$,$N=2$,$3$,$4$,即:$$A(N,K)={a|a=a_1a_2a_3\cdots a_N,0≤a_i≤K-1,i=1,\cdots,N}$$ 设$D(N-1,K)$是$A(N-1,K)$的一个子集,它是由$A(N,K)$生成的一个$N-1$位$K$进制整数$d$的集合,**生成规则如下**: 对任何$d\in D(N-1,K)$,存在$a\in A(N,K)$,使$d=Image(a)$,其中,$d=d_1d_2\cdots d_{N-1},d_i=min(a_i,a_{i+1})$,即$d_i$取为$a_i,a_{i+1}$的最小值。 注1:我们称这个规则为$A(N,K)$ 到$A(N-1,K)$内的一个映射$d=Image(a)$,可以证明这个映射是多对一的,即:如果$d,e\in D(N-1,k)$且$d\not=e$,则对任何满足$d=Image(a),e=Image(c)$的$A(N,K)$中的元素$a,c$,均有$a\not=c$ 注2:对某些$K,N$, $D(N-1,K)$是$A(N-1,K)$的一个真子集,例如$K=4,N=4$,则不存在$a\in A(4,4)$,使$Image(a)=(323)$ **任务**:从文本文件输入两个用空格隔开的整数 $N,K$,然后在指定的文本文件中输出下列表达式的值: $$f(N,K)=\sum_{a\in A(N,K),Image(a)=d}(\prod_{i=1}^{N-1}(d_i+1))$$ 上式表示对$A(N,K)$中的全部元素$a$,对其映像$d=Image(a)=d_1d_2\cdots d_{N-1}$的各位数字加$1$后的乘积求和。 其中$\prod^{N-1}_{i=1}(d_i+1)=(d_1+1)(d_2+1)\cdots(d_{N-1}+1)$ **例**:设$N=2,K=3$,则$A(N,K)={00,01,02,11,10,12,20,21,22}$,正确的输出结果应为$14$。 **提示**:应先建立相应的计算方法,直接利用$f(N,K)$的表达式计算会使多数测试超时。 ## 输入格式 输入文件只有一行:用空格隔开的两个整数N k。 ## 输出格式 输出文件只有一个大整数,为计算结果。 ## 输入输出样例 #1 ### 输入 #1 ``` 2 3 ``` ### 输出 #1 ``` 14 ``` ## 说明/提示 **关于测试的说明**: 数字完全正确,给满分。当输出结果的位数超过$15$位时,如果仅最后两位不准确时给一半分。(每个需测试的计算结果不超过$10^{19}$)。
最新发布
03-10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值