题目链接
Solution
我只会60分暴力...
正解是 DP.
状态定义:
(f[i][j]) 代表 (1) 到 (i) 比最短路长 (j) 的方案数.
那么很显然最后答案也就是 (sum^{i=0}_{k}f[n][i]).
转移方程:
对于任一状态 (f[i][j]) 我们对可以到达它的点 (v) 进行讨论:
- (v) 本身为 (1) 到 (i) 的最短路上的节点,则此时 $$f[i][j]+=f[v][j]$$
- 若 (v) 并非到其最短路上的点.
那么此时从 (v) 到 (i) 相当于多走了((dis[i]-(dis[v]+w_{v,i})))这么长.
所以此时 $$f[i][j]+=f[v][j-(dis[i]-(dis[v]+w_{v,i}))]$$
然后很明显 (1) 也可以表示为 (2) 状态.
所以 (2) 状态即为总动态转移方程.
(0) 环:
由于题目中给出的图并非一张 (DAG) ,所以可能存在 (0) 环的情况.
如果 (DP) 从前往后推,那么可以使用拓扑排序.
记忆化搜索则需要判断一种状态是否在一次搜索时出现多次.
此处给出记忆化搜索的代码.
Code
#include<bits/stdc++.h>
#define in(x) x=read()
#define ll long long
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=100008;
struct node{int u; ll d;
bool operator <(const node& kkk)const
{return d>kkk.d;}
};
struct sj{int to,next;ll w;}a[maxn*4];
int head[maxn],size,Head[maxn];
ll f[maxn][52],dis[maxn],ans;
int n,m,k,mod,c[maxn][52],ff;
int v[maxn][52],vis[maxn];
int read()
{
char ch=getchar(); int f=1,w=0;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
return f*w;
}
void add(int x,int y,int w)
{a[++size].to=y;a[size].next=head[x];head[x]=size;a[size].w=w;}
void Add(int x,int y,int w)
{a[++size].to=y;a[size].next=Head[x];Head[x]=size;a[size].w=w;}
void Dijkstra()
{
priority_queue<node>q;
memset(dis,127,sizeof(dis));
q.push((node){1,0});
dis[1]=0;
while(!q.empty())
{
node x=q.top();q.pop();
int u=x.u;
for(int i=head[u];i;i=a[i].next)
{
int tt=a[i].to;
if(dis[tt]>dis[u]+a[i].w)
{
dis[tt]=dis[u]+a[i].w;
q.push((node){tt,dis[tt]});
}
}
}
return;
}
ll dfs(int x,int kk)
{
if(v[x][kk])return f[x][kk];
v[x][kk]=1;
c[x][kk]=1;
if(ff==1)return 0;
for(int i=Head[x];i;i=a[i].next)
{
int tt=a[i].to;
int t=dis[x]+kk-a[i].w-dis[tt];
if(t<0)continue;
if(c[tt][t]){ff=1;return 0;}
f[x][kk]+=dfs(tt,t);
f[x][kk]%=mod;
}
c[x][kk]=0;
return f[x][kk];
}
int main()
{
int t; in(t);
while(t--)
{
//Init
memset(c,0,sizeof(c));
memset(v,0,sizeof(v));
memset(dis,127,sizeof(dis));
memset(Head,0,sizeof(Head));
memset(head,0,sizeof(head));
memset(a,0,sizeof(a)); size=0;
memset(f,0,sizeof(f)); ff=0;
//Input
in(n); in(m); in(k); in(mod);
for(int i=1;i<=m;i++)
{
int x,y,z; in(x),in(y),in(z);
add(x,y,z); Add(y,x,z);
}
Dijkstra();
//dfs + DP
ans=0;
f[1][0]=v[1][0]=1;
for(int i=0;i<=k;i++)
{
dfs(n,i),ans+=f[n][i],ans%=mod;
if(ff==1)break;
}
dfs(n,k+1);
if(ff==1)
{printf("-1
");continue;}
printf("%lld
",ans);
}
}