hdu 4396 More lumber is required

题目大意:

n个点,m条双向边。给你起点s,终点t,以及k。问你从s到t至少得到k分的最短路。边有自环,且边可以重复走。边每走一次获得10分。

(1<=N<=5000)(0<=M<=100000) (1<=S, T<=N; 0<=K<=500)

题解:

二维spfa,d[x][j]表示到x这个点经过j条边的最短路,有个重要的优化是:当L>K时把它并入到走K条边的状态中。

比赛时一直用拆点+一维spfa写,一直TLE或MLE……方法整个错了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<utility>
using namespace std;
queue< pair<int,int> >q;
const int inf=1<<29;
const int maxn=1000000;
int to[maxn],edge[maxn],cap[maxn],next[maxn],d[5001][100];
bool v[5001][100];
int em,n,m,maxk;
inline int change(int x)
{
    if(x<=maxk)return x;else return maxk;
}
void addedge(int x,int y,int d)
{
    to[em]=y;
    cap[em]=d;
    next[em]=edge[x];
    edge[x]=em++;
}
void spfa(int s)
{
    for(int i=0;i<=n;i++)
    for(int j=0;j<=51;j++)
    {
        d[i][j]=inf;
        v[i][j]=false;
    }
    d[s][0]=0;v[s][0]=true;q.push(make_pair(s,0));
    while(!q.empty())
    {
        int x=q.front().first,j=q.front().second;q.pop();
        int p=edge[x];
        while(p!=-1)
        {
            if(d[x][j]+cap[p]<d[to[p]][change(j+1)])
            {
                d[to[p]][change(j+1)]=d[x][j]+cap[p];
                if(!v[to[p]][change(j+1)])
                {
                    v[to[p]][change(j+1)]=true;
                    q.push(make_pair(to[p],change(j+1)));
                }
            }
            p=next[p];
        }
        v[x][j]=false;
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        em=0;
        memset(edge,-1,sizeof(edge));
        memset(next,-1,sizeof(next));
        for(int i=1;i<=m;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        int s,t;
        scanf("%d%d%d",&s,&t,&maxk);
        if(maxk%10==0)maxk=maxk/10;else maxk=(maxk/10)+1;
        spfa(s);
        if(d[t][maxk]<inf)printf("%d\n",d[t][maxk]);else printf("-1\n");
    }
    return 0;
}


你可能感兴趣的:(hdu 4396 More lumber is required)