【bzo1579】拆点+dijkstra优先队列优化+其他优化

【bzo1579】拆点+dijkstra优先队列优化+其他优化

题意:

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\n",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\n",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\n",ans);
117     return 0;
118 }

 

posted @ 2016-10-24 21:12 拦路雨偏似雪花 阅读( ...) 评论( ...) 编辑 收藏
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值