题意:
n个点,m条边,问从1走到n的最短路,其中有K次机会可以让一条路的权值变成0。
1≤N≤10000;1≤M≤500000;1≤K≤20
题解:
拆点,一个点拆成K个,分别表示到了这个点时还有多少次机会。
(x,k)-->(y,k-1),cost=0 或 (x,k)-->(y,k),cost=a[i].d;
这题数据比较大, 需要很多优化。(应该只是蒟蒻我才需要这么多优化。。)
1.不用spfa(时间复杂度不稳定),用dijkstra+优先队列优化
2.拆点不拆边。g[i]表示i这个点是由谁拆分出来的,id[i]表示i这个点表示还能用几次,st[i]表示i拆分出来的点的编号从什么开始,图就按照原图建立,比如(x,k)-->(y,k-1)就可以直接找到st[x]+k -- > st[y]+k-1。
3.如果“目标点n 用完了K次机会”这个状态到1的最短路已经算出来了,那一定是最优的,其它的都不用算了。加了这句话从10s+跑到了0.6s。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<iostream>
5 #include<algorithm>
6 #include<queue>
7 #include<vector>
8 #include<cmath>
9 using namespace std;
10
11 typedef long long LL;
12 const int N=2*20*10010,M=500010;//不知道为什么点要开两倍才能过。。。
13 const LL INF=(LL)1e15;
14 struct node{
15 int x,y,next;
16 LL d;
17 }a[2*M];
18 int n,m,K,len,num,first[N],g[N],id[N],st[N];
19 LL dis[N],mn[N];
20 struct point{int x;LL d;};
21 struct cmp{
22 bool operator () (point &x,point &y){return x.d>y.d;}
23 };
24 priority_queue<point,vector<point>,cmp> q;
25
26 LL minn(LL x,LL y){return x<y ? x:y;}
27
28 void ins(int x,int y,LL d)
29 {
30 a[++len].x=x;a[len].y=y;a[len].d=d;
31 a[len].next=first[x];first[x]=len;
32 }
33
34 void dijkstra()
35 {
36 int y,bk=0;
37 point t;
38 for(int i=1;i<=num;i++) dis[i]=-1;
39 // for(int i=1;i<=num;i++) if(dis[i]!=-1) printf("%d g = %d id = %d dis = %lld
",i,g[i],id[i],dis[i]);
40
41 memset(mn,127,sizeof(mn));
42 while(!q.empty()) q.pop();
43 for(int i=0;i<=K;i++) dis[st[1]+i]=0;
44
45 for(int i=first[1];i;i=a[i].next)
46 {
47 for(int j=0;j<=K;j++)
48 {
49 t.x=st[a[i].y]+j;
50 t.d=dis[st[1]+j]+a[i].d;
51 if(mn[t.x]>t.d) mn[t.x]=t.d,q.push(t);//如果原来这个点已经可以被更优的所更新,那就不放到队列里面。
52 if(j>=1)
53 {
54 t.x=st[a[i].y]+j-1;
55 t.d=dis[st[1]+j];
56 if(mn[t.x]>t.d) mn[t.x]=t.d,q.push(t);
57 }
58 }
59 }
60 while(!q.empty() && !bk)
61 {
62 while(!q.empty())
63 {
64 t=q.top();q.pop();
65 if(dis[t.x]==-1)
66 {
67 dis[t.x]=t.d;
68 y=t.x;
69 if(y==st[n]) bk=1;
70 //如果“目标点n 用完了K次机会”这个状态到1的最短路已经算出来了,那一定是最优的,其它的都不用算了。加了这句话从10s+跑到了0.6s。
71 break;
72 }
73 }
74 for(int i=first[g[y]];i;i=a[i].next)
75 {
76 t.x=st[a[i].y]+id[y];
77 t.d=dis[y]+a[i].d;
78 if(dis[t.x]==-1 && mn[t.x]>t.d) mn[t.x]=t.d,q.push(t);
79 if(id[y]>=1)
80 {
81 t.x=st[a[i].y]+id[y]-1;
82 t.d=dis[y];
83 if(dis[t.x]==-1 && mn[t.x]>t.d) mn[t.x]=t.d,q.push(t);
84 }
85 }
86 }
87 }
88
89 int main()
90 {
91 // freopen("a.in","r",stdin);
92 freopen("revamp.in","r",stdin);
93 freopen("revamp.out","w",stdout);
94 scanf("%d%d%d",&n,&m,&K);
95 len=0;num=0;
96 memset(first,0,sizeof(first));
97 for(int i=1;i<=m;i++)
98 {
99 int x,y;LL d;
100 scanf("%d%d%lld",&x,&y,&d);
101 ins(x,y,d);
102 ins(y,x,d);
103 }
104 for(int i=1;i<=n;i++)
105 for(int j=0;j<=K;j++)
106 {
107 g[++num]=i,id[num]=j;
108 if(j==0) st[i]=num;
109 }
110
111 dijkstra();
112 // for(int i=1;i<=num;i++) if(dis[i]!=-1) printf("%d g = %d id = %d dis = %lld
",i,g[i],id[i],dis[i]);
113 LL ans=INF;
114 for(int i=st[n];i<=st[n]+K;i++)
115 if(dis[i]!=-1) ans=minn(ans,dis[i]);
116 printf("%lld
",ans);
117 return 0;
118 }