题目链接:https://www.luogu.org/problem/P3953
30分
1.SPFA先找最短边
2.用dp转移方程 f[x][L]->f[y][L+w(x,y)],求f[n][dis(1,n)-dis(1,n)+k]
70分
然后我们就发现了dp的状态其实没有那么多。如果中间过程已经超过了k的话,就没有必要继续下去了。
即优化:只有dis(1,x)<=L<=dis(1,x)+k是有意义的
(qwq就是这个我也调了好久www)
Q1:为啥1,2两层循环不能交换???
Q2:另外一种写法有啥问题啊啊啊。。。
Q3:如何给这些点排序???(也就是按什么顺序dp)
先贴代码。。。(我知道你们只关注这个www)
#include <iostream>
#include <vector>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn=0x3f3f3f3f;
int n,m,k,p,flag;
struct node
{
int x,y,w;
};
vector<node> v[100005];
int f[100005][55];
int res[100005];
int dis[100005],cnt[100005];
int cmp(int x,int y)
{
return dis[x]<dis[y];
}
void spfa(int s)
{
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++) dis[i]=maxn;
int inque[100005];
queue<int> q;
q.push(s);
dis[s]=0;
inque[s]=true;
while(!q.empty())
{
int x=q.front();
// if(cnt[x]>n) {flag=1;return;}
q.pop();
inque[x]=false;
for(int i=0;i<v[x].size();i++)
{
if(dis[v[x][i].y]>v[x][i].w+dis[x])
{
// cnt[v[x][i].y]=cnt[x]+1;
// if(cnt[v[x][i].y]>n) {flag=1;return;}
dis[v[x][i].y]=v[x][i].w+dis[x];
if(!inque[v[x][i].y])
{
inque[v[x][i].y]=true;
q.push(v[x][i].y);
}
}
}
}
}
int main()
{
int t;
cin>>t;
for(int round=1;round<=t;round++)
{
for(int i=1;i<=n;i++) dis[i]=maxn;
cin>>n>>m>>k>>p;
for(int i=1;i<=n;i++)
v[i].clear();
for(int i=1;i<=m;i++)
{
node k;
cin>>k.x>>k.y>>k.w;
v[k.x].push_back(k);
}
memset(f,0,sizeof(f));
spfa(1);
if(flag) printf("%-1\n");
f[1][0]=1;
for(int i=1;i<=n;i++) res[i]=i;
sort(res+1,res+1+n,cmp);
for(int j=0;j<=k;j++)
{
for(int uu=1;uu<=n;uu++)
{
int u=res[uu];
for(int i=0;i<v[u].size();i++)
{
int y=v[u][i].y;//v
int temp=dis[u]+v[u][i].w-dis[y];
if(temp+j>k) continue;
else (f[y][temp+j]+=f[u][j])%=p;
}
}
}
int ans=0;
for(int i=0;i<=k;i++)
{
ans+=f[n][i];
ans%=p;
}
cout<<ans<<endl;
}
return 0;
}
100分
100分在70分的基础上多了三个有0边的点,这样的话就引出了2个新的问题
Q1.如何处理0边两端的点的先后顺序
Q2.如何判断是否有无穷多种路径(也就是是否有0环)
Q3:如何卡常???