逛公园 [NOIP2017 D1T3] [记忆化搜索]
Description
策策同学特别喜欢逛公园。公园可以看成一张N个点M条边构成的有向图,且没有自环和重边。其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从1号点进去,从N号点出来。
策策喜欢新鲜的事物,他不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,他不希望每天在逛公园这件事上花费太多的时间。如果1号点到N号点的最短路长为d,那么策策只会喜欢长度不超过d+K的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮他吗? 为避免输出过大,答案对P取模。 如果有无穷多条合法的路线,请输出−1。
Input
第一行包含一个整数 T, 代表数据组数。 接下来T组数据,对于每组数据:
第一行包含四个整数 N,M,K,P,每两个整数之间用一个空格隔开。
接下来M行,每行三个整数ai,bi,ci,代表编号为ai、bi的点之间有一条权值为ci的有向边,每两个整数之间用一个空格隔开。
Output
包含T行,每行一个整数代表答案。
Sample Input
2
5 7 2 10
1 2 1
2 4 0
4 5 2
2 3 2
3 4 1
3 5 2
1 5 3
2 2 0 10
1 2 0
2 1 0
Sample Output
3
-1
说明
【样例解释1】
对于第一组数据,最短路为 3 。 1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5 为 3条合法路径。
【测试数据与约定】
对于不同的测试点,我们约定各种参数的规模不会超过如下
测试点编号 | TT | NN | MM | KK | 是否有0边 |
---|---|---|---|---|---|
1 | 5 | 5 | 10 | 0 | 否 |
2 | 5 | 1000 | 2000 | 0 | 否 |
3 | 5 | 1000 | 2000 | 50 | 否 |
4 | 5 | 1000 | 2000 | 50 | 否 |
5 | 5 | 1000 | 2000 | 50 | 否 |
6 | 5 | 1000 | 2000 | 50 | 是 |
7 | 5 | 100000 | 200000 | 0 | 否 |
8 | 3 | 100000 | 200000 | 50 | 否 |
9 | 3 | 100000 | 200000 | 50 | 是 |
10 | 3 | 100000 | 200000 | 50 | 是 |
数据保证:至少存在一条合法的路线。
分析
我真的是太弱了,考场上写了一个A星,结果过的全是K=0的,然后就只有30分了(t1t2已让我心态爆炸)
我们看数据,K≤50,我们可以比较容易地想到dp,如何设状态?
我们记dis[u]为u点到n的最短路,我们走完一条方案希望整个距离不超过dis[1]+K
那我们就记录下到每个点的剩余额度rem,我们希望剩余额度尽可能大,就有更多精力走更多的路
对于一条边edge(u,v,val)
如果不走这条边,从u出发的最小代价为dis[u](记录最小代价就是为了剩余额度尽可能大,到下个路口有更多的选择)
如果我们走了这条边,从u出发的最小代价为val+dis[v]
那么剩余额度就是rem-(dis[v]+val-dis[u]),如果(dis[v]+val-dis[u])>rem 此方案无效
那么我们看出来方案数和当前所在的点u和剩余额度rem有关,那么我们记录方案数为dp[u][rem],根据上面分析可以知道dp[u][rem]+=dp[v][rem-(dis[v]+val-dis[u])],记忆化搜索即可。
到了终点不论剩余额度是多少,一开始给了k的额度,只要最后的额度≥0即可。
如何对付0边?记录下状态vis[u][rem]即可,如果再次访问就返回-1
代码
1 #include<set> 2 #include<map> 3 #include<queue> 4 #include<stack> 5 #include<cmath> 6 #include<cstdio> 7 #include<cstring> 8 #include<iostream> 9 #include<algorithm> 10 #define RG register int 11 #define rep(i,a,b) for(RG i=a;i<=b;++i) 12 #define per(i,a,b) for(RG i=a;i>=b;--i) 13 #define ll long long 14 #define inf (1<<29) 15 #define maxn 100005 16 #define maxm 200005 17 #define add(x,y,z) e[++cnt]=(E){y,head[x],z},head[x]=cnt 18 using namespace std; 19 int T,n,m,K,p,cnt,ans,flg; 20 int head[maxn],vis[maxn][55],dp[maxn][55],dis[maxn]; 21 struct E{ 22 int v,next,val; 23 }e[maxm]; 24 inline int read() 25 { 26 int x=0,f=1;char c=getchar(); 27 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 28 while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} 29 return x*f; 30 } 31 32 void init() 33 { 34 memset(head,0,sizeof(head)); 35 memset(dis,63,sizeof(dis)); 36 memset(vis,0,sizeof(vis)); 37 memset(dp,0,sizeof(dp)); 38 cnt=ans=flg=0; 39 } 40 41 inline int mod(int a){return a>=p?a-p:a;} 42 43 namespace PRE{ 44 int head[maxn],vis[maxn],cnt; 45 46 struct E{ 47 int v,next,val; 48 }e[maxm]; 49 50 void ad(int x,int y,int z) {e[++cnt]=(E){y,head[x],z},head[x]=cnt;} 51 52 void init(){memset(head,0,sizeof(head));cnt=0;} 53 54 void spfa() 55 { 56 queue<int> que;que.push(n),dis[n]=0; 57 RG u,v; 58 while(!que.empty()) 59 { 60 u=que.front(),que.pop(); 61 for(RG i=head[u];i;i=e[i].next) 62 { 63 v=e[i].v; 64 if(dis[v]>dis[u]+e[i].val) 65 { 66 dis[v]=dis[u]+e[i].val; 67 if(!vis[v]) vis[v]=1,que.push(v); 68 } 69 } 70 vis[u]=0; 71 } 72 } 73 74 } 75 76 int dfs(int u,int rem) 77 { 78 if(dp[u][rem]) return dp[u][rem]; 79 if(vis[u][rem]) return dp[u][rem]=-1; 80 vis[u][rem]=-1; 81 int res,v,ret; 82 if(u==n) dp[u][rem]=1; 83 for(int i=head[u];i;i=e[i].next) 84 { 85 v=e[i].v; 86 res=dis[v]+e[i].val-dis[u]; 87 if(res>rem) continue; 88 if((ret=dfs(v,rem-res))==-1) return dp[u][rem]=-1; 89 dp[u][rem]=mod(ret+dp[u][rem]); 90 } 91 vis[u][rem]=0; 92 return dp[u][rem]; 93 } 94 95 int main() 96 { 97 freopen("data7.in","r",stdin); 98 T=read(); 99 while(T--) 100 { 101 init();PRE::init(); 102 n=read(),m=read(),K=read(),p=read(); 103 for(RG i=1,u,v,val;i<=m;i++) u=read(),v=read(),val=read(),add(u,v,val),PRE::ad(v,u,val); 104 PRE::spfa(); 105 memset(vis,0,sizeof(dp)); 106 ans=dfs(1,K);printf("%d\n",ans); 107 } 108 return 0; 109 }