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