【题面】:
逛公园
思路:
此题还是很有挑战性,考场上绝大部分人都只打了暴力((#include<me>))。
首先我觉得(k=0)时(30)分的暴力还是比较简单,和最短路计数一样,直接暴力就(ok) :
#include<cstdio>
#include<queue>
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;
const int MAXM = 200005;
const int MAXN = 100005;
int n,m,k,p;int ans=0;int minn;
struct edge{
int u,v,w,nxt;
}e[MAXM<<1];int head[MAXN];int cnt=0;int dis[MAXN];bool r[MAXN];bool vis[MAXN];
inline void add(int u,int v,int w){
e[++cnt].u = u;e[cnt].v = v;e[cnt].w = w;e[cnt].nxt = head[u];head[u] = cnt;
}
inline void spfa(){
queue<int>q;
q.push(1);
memset(dis,inf,sizeof dis);
dis[1] = 0;
while(!q.empty()){
int u = q.front();q.pop();r[u] = 0;
for(int i=head[u];i;i=e[i].nxt){
int v = e[i].v;
if(dis[v] > dis[u] + e[i].w){
dis[v] = dis[u] + e[i].w;
if(!r[v]){
r[v] = 1;
q.push(v);
}
}
}
}
}
inline void dfs(int x,int len){
if(len > minn + k) return ;
if(x == n){
ans++;
ans %= p;
return ;
}
vis[x] = 1;
for(int i=head[x];i;i=e[i].nxt){
int v = e[i].v;
if(!vis[v]){
dfs(v,len+e[i].w);
vis[v] = 0;
}
}
}
inline void clear(){
memset(head,0,sizeof head);
cnt=0;
memset(dis,0,sizeof dis);
memset(r,0,sizeof r);
memset(vis,0,sizeof vis);
ans=0;
}
int main(){
int T;scanf("%d",&T);
while(T--){
clear();
scanf("%d%d%d%d",&n,&m,&k,&p);
for(int i=1;i<=m;++i){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
spfa();
minn = dis[n];
dfs(1,0);
printf("%d
",ans%p);
}
return 0;
}
然后考虑满分做法,直接暴力时我们发现,有很多节点是被重复访问了,于是可以考虑记忆化搜索。用(f[i][j])表示到(i)这个节点,多走了(j)的路程的方案数,(obviously) (0<=j<=k),应为(k_{max}=50),所以该状态的定义可以表示。然后从(1)到(k)扫一遍,记录下所有(f[i][j])的值。
那么如何判断(0)环呢?就记录一个(vis[i][j])的数组,定义和(f)一样,如果当前访问的节点已经(vis)过了,就说明有(0)环,记得最后还要跑一遍(dfs(n,k+1)),因为有些(0)环的情况可能未被更新到。
#include<cstdio>
#include<queue>
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;
int T;int n,m,k,p;
const int MAXN = 100005;
const int MAXM = 200005;
struct edge{
int u,v,w,nxt;
}e[MAXM],E[MAXM];int Hd[MAXN];int head[MAXN];int cnt=0;
inline void add(int u,int v,int w){
e[++cnt].u = u;e[cnt].v = v;e[cnt].w = w;e[cnt].nxt = head[u];head[u] = cnt;
E[cnt].u = v;E[cnt].v = u;E[cnt].w = w;E[cnt].nxt = Hd[v];Hd[v] = cnt;
}
queue<int>q;int dis[MAXN];bool r[MAXN];
int f[MAXN][51],vis[MAXN][51];bool err = 0;
inline void spfa(){
q.push(1);memset(dis,inf,sizeof dis);
dis[1] = 0;
while(!q.empty()){
int u = q.front();q.pop();r[u] = 0;
for(int i=head[u];i;i=e[i].nxt){
int v = e[i].v;
if(dis[v] > dis[u] + e[i].w){
dis[v] = dis[u] + e[i].w;
if(!r[v]){
r[v] = 1;
q.push(v);
}
}
}
}
}
inline int dfs(int u,int waste){
if(~f[u][waste]) return f[u][waste];
vis[u][waste] = 1;
f[u][waste] = 0;
for(int i=Hd[u];i;i=E[i].nxt){
int v = E[i].v;
int left = dis[u]-dis[v]+waste-E[i].w;
if(left < 0) continue;
if(vis[v][left]) err = 1;
f[u][waste] += dfs(v , left);
f[u][waste] %= p;
}
vis[u][waste] = 0;
return f[u][waste];
}
inline void calc(){
f[1][0] = 1;int ans = 0;
for(int i=0;i<=k;++i){
ans += dfs(n,i),ans%=p;
}
dfs(n,k+1);
if(err){puts("-1");return ;}
printf("%d
",ans);
}
inline void clear(){
memset(head,0,sizeof head);
memset(Hd,0,sizeof Hd);
memset(vis,0,sizeof vis);
memset(f,-1,sizeof f);
memset(r,0,sizeof r);
err = 0;cnt = 0;
}
int main(){
scanf("%d",&T);
while(T--){
clear();
scanf("%d%d%d%d",&n,&m,&k,&p);
for(int i=1;i<=m;++i){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
spfa();
calc();
}
return 0;
}