题目大意:
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;
}