題目鏈接: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:如何卡常???