【BZOJ】1579: [Usaco2009 Feb]Revamping Trails 道路升级

【算法】分层图最短路

【题解】

考虑k层一模一样的图,然后每个夹层都在每条边的位置新加从上一层跨越到下一层的边权为0的边,这样至多选择k条边置为0。

然后考虑方便的写法。

SPFA

第一次SPFA计算常规最短路(顶层)。

之后k次SPFA,松弛操作加上可以从上一层节点直接获取最短路(即相当于省一条边)

这样可以保证一次SPFA最多只有一条边省略,因为你要么从上一层前一个点下来,其实是获取上一层前一个点的最短路。

要么从前面一个点过来,其实是获取本层的最短路,本层最短路最多从上面下来一次。

因为只与上一层有关,开滚动数组。

SPFA记得SLF优化,不然较慢!

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxm=50010,maxn=10010;
struct edge{int v,w,from;}e[maxm*3];
int first[maxn],X,n,m,k,q[10010],tot=0;
long long d[2][maxn];
bool vis[maxn];
void insert(int u,int v,int w)
{tot++;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;}
void spfa()
{
    memset(vis,0,n+1);
    memset(d[X],0x3f,8*(n+1));
    int head=0,tail=1;q[0]=1;vis[1]=1;d[X][1]=0;
    while(head!=tail)
     {
         int x=q[head++];if(head>10000)head=0;
         for(int i=first[x];i;i=e[i].from)
          if(d[X][e[i].v]>d[X][x]+e[i].w)
           {
               int y=e[i].v;
               d[X][y]=d[X][x]+e[i].w;
               if(!vis[e[i].v])
                {
                    if(d[X][y]<d[X][q[head]]){head--;if(head<0)head=10000;q[head]=y;}
                 else{q[tail++]=y;if(tail>10000)tail=0;}
                    vis[e[i].v]=1;
                }
           }
         vis[x]=0;
     }
//    for(int i=1;i<=n;i++)printf("%d ",d[X][i]);printf("\n");
}
void spfas()
{
//    for(int i=1;i<=n;i++)d[X][i]=d[1-X][i];
    memset(d[X],0x3f,8*(n+1));
    int head=0,tail=1;q[0]=1;vis[1]=1;d[X][1]=0;
    while(head!=tail)
     {
         int x=q[head++];if(head>10000)head=0;//printf("q %d",x);
         for(int i=first[x];i;i=e[i].from)
          if(d[X][e[i].v]>min(d[X][x]+e[i].w,d[1-X][x]))
           {
               int y=e[i].v;
               d[X][y]=min(d[X][x]+e[i].w,d[1-X][x]);
               if(!vis[e[i].v])
                {
                    if(d[X][y]<d[X][q[head]]){head--;if(head<0)head=10000;q[head]=y;}
                 else{q[tail++]=y;if(tail>10000)tail=0;}
                    vis[e[i].v]=1;
                }
           }
         vis[x]=0;
     }
//    for(int i=1;i<=n;i++)printf("%d ",d[X][i]);printf("\n");
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++)
     {
         int u,v,w;
         scanf("%d%d%d",&u,&v,&w);
         insert(u,v,w);
         insert(v,u,w);
     }
    X=0;
    spfa();
    for(int i=1;i<=k;i++)
     {
         X=1-X;
         spfas();
     }
    printf("%lld",d[X][n]);
    return 0;
}
View Code

Dijkstra

效率相似,但是写法简单很多,只要记录多一维层次,每次更新的时候附带上到下一层的更新,然后根据dij每次选择最短的更新的特点,第一次到达n就是答案了。

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=100010;
struct edge{int v,w,from;}e[maxn];
struct cyc{
    int x,k,d;
    bool operator < (const cyc &a)const{
        return d>a.d;
    }
};
priority_queue<cyc>q;
int n,m,first[maxn],tot,d[maxn][30],kind;


void insert(int u,int v,int w){
    tot++;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;
    tot++;e[tot].v=u;e[tot].w=w;e[tot].from=first[v];first[v]=tot;
}
int dijkstra(){
    q.push((cyc){1,0,0});
    memset(d,0x3f,sizeof(d));
    d[1][0]=0;
    while(!q.empty()){
        cyc x=q.top();q.pop();
        if(x.d!=d[x.x][x.k])continue;
        if(x.x==n)return x.d;
        for(int i=first[x.x];i;i=e[i].from){
            if(d[e[i].v][x.k]>d[x.x][x.k]+e[i].w){d[e[i].v][x.k]=d[x.x][x.k]+e[i].w;q.push((cyc){e[i].v,x.k,d[e[i].v][x.k]});}
            if(x.k<kind&&d[e[i].v][x.k+1]>d[x.x][x.k]){d[e[i].v][x.k+1]=d[x.x][x.k];q.push((cyc){e[i].v,x.k+1,d[e[i].v][x.k+1]});}
        }
    }
    return 0;
}
int main(){
    scanf("%d%d%d",&n,&m,&kind);
    int u,v,w;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        insert(u,v,w);
    }
    printf("%d",dijkstra());
    return 0;
}
View Code

 

posted @ 2017-08-29 21:19  ONION_CYC  阅读(329)  评论(0编辑  收藏  举报