记搜、最短路--NOIP2017 逛公园

luogu3953

solution:
一开始看到的时候最短路计数的时候还疑惑了一下
因为之前做的只有最小生成树计数,图上路径计数怎么搞啊
然后感觉是类似搜索的东西可以搞一搞
忍不住看了下标签:记忆化搜索
嗷!原来这样
但是一开始定义的f数组是一维的只记录点
但是立马就发现这样不行了
因为一个点可能有多种路长
但是路长太长了没法记啊

所以我们应该要记比最短路多出去的那一部分呀!!!
因为k最大只有50哎呀这不就完了
这个我是倒着搜的,正着也能做
还有我们要判一下环,如果f[u][kk]已经被访问过了说明有0环
这个时候走多少次0环都可以,所以输出-1
然后可以算出下一个点要比最短路多出多少,根据这个dfs下一个点
如果f不是-1说明已经被dfs过了就返回这个值
很普通的记搜操作呀
然后就是初始化啊细节之类的问题了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define N 100005
#define M 200005
using namespace std;
int t,n,m,k,p,cnt,head[N],cnt2,head2[N],dis[N],f[N][55],ans;
bool vis[N],flg,used[N][55];
priority_queue< pair<int,int> > q;

inline int rd(){
    int x=0,f=1;char c=' ';
    while(c<'0' || c>'9') {if(c=='-')f=-1;c=getchar();}
    while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
    return x*f;
}

struct EDGE{
    int to,nxt,w;
}edge[M],e[M]; 

void add(int x,int y,int z){
    edge[++cnt].to=y;
    edge[cnt].nxt=head[x];
    edge[cnt].w=z;
    head[x]=cnt;
}

void add2(int x,int y,int z){
    e[++cnt2].to=y;
    e[cnt2].nxt=head2[x];
    e[cnt2].w=z;
    head2[x]=cnt2;
}

void dijkstra(){
    memset(dis,0x3f,sizeof dis);  memset(vis,0,sizeof vis);
    dis[1]=0;
    while(!q.empty()) q.pop();
    q.push(make_pair(0,1));
    while(!q.empty()){
        int x=q.top().second; q.pop();
        if(vis[x]) continue;
        vis[x]=1;
        for(int i=head[x];i;i=edge[i].nxt){
            int y=edge[i].to,z=edge[i].w;
            if(dis[y]>dis[x]+z) dis[y]=dis[x]+z,q.push(make_pair(-dis[y],y));
        }
    }
}

int dfs(int u,int kk){
    if(flg) return 0;
    if(u==n+1 && kk==0) return 1;
    if(f[u][kk]!=-1) return f[u][kk];
    f[u][kk]=0;
    if(used[u][kk]) {flg=true;return 0;}
    used[u][kk]=1;
    for(int i=head2[u];i;i=e[i].nxt){
        int v=e[i].to;
        int nxk=kk+dis[u]-dis[v]-e[i].w;
        if(nxk>=0 && nxk<=k){
            if(used[v][nxk]) {flg=true;return 0;}
            (f[u][kk]+=dfs(v,nxk))%=p;
        }
    }
    used[u][kk]=0;
    return f[u][kk];
}

void init(){
    memset(f,-1,sizeof f); ans=cnt=cnt2=0; memset(used,0,sizeof used); flg=false;
    memset(head,0,sizeof head); memset(head2,0,sizeof head2);
}

int main(){
    t=rd();
    while(t--){
        init();
        n=rd(); m=rd(); k=rd(); p=rd();
        for(int i=1;i<=m;i++){
            int x=rd(),y=rd(),z=rd();
            add(x,y,z); add2(y,x,z);
        }
        dijkstra();
        dis[n+1]=0; add2(1,n+1,0);//这里是因为1到n本来就有一条最短路 
        for(int i=0;i<=k;i++){
            (ans+=dfs(n,i))%=p;
            if(flg) break;
        }
        if(flg) printf("-1\n");
        else printf("%d\n",ans);
    } 
    return 0;
} 

正着搜是这么来的!

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
using namespace std;
const int N=100010;
const int inf=0x3f3f3f3f;
typedef long long ll;
inline int read(){
    int r=0,f=1,c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){r=r*10+c-'0';c=getchar();}
    return r*f;
}
struct Edge{
    int to,nxt,w;
}e[N*2],E[N*2];
int head[N],Head[N],cnt=1;
void add(int u,int v,int w){
    e[cnt]=(Edge){v,head[u],w};
    head[u]=cnt;
    E[cnt]=(Edge){u,Head[v],w};
    Head[v]=cnt++;
}
int n,m,K,P;
bool inq[N];
int dis[N],f[N][51],c[N][51];
void spfa(){
    deque<int>q;
    memset(dis,63,sizeof dis);
    dis[1]=0;q.push_back(1);inq[1]=1;
    while(!q.empty()){
        int u=q.front();q.pop_front();inq[u]=0;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to,w=e[i].w;
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                if(!inq[v]){
                    if(!q.empty()&&dis[v]<=dis[q.front()])
                    q.push_front(v);
                    else q.push_back(v);
                }
            }
        }
    }
}bool ff=0;
void Clear(){
    n=m=K=P=0;
    memset(inq,0,sizeof inq);
    memset(f,-1,sizeof f);
    memset(head,0,sizeof head);
    memset(Head,0,sizeof Head);
    memset(c,0,sizeof c);
    memset(e,0,sizeof e);
    memset(E,0,sizeof E);cnt=1;
    ff=0;
}
void Init(){
    n=read(),m=read(),K=read(),P=read();
    while(m--){
        int u=read(),v=read(),w=read();
        add(u,v,w);
    }
}

int dfs(int u,int k){
    if(~f[u][k])return f[u][k];
    c[u][k]=1;
    f[u][k]=0;
    for(int i=Head[u];i;i=E[i].nxt){
        int v=E[i].to,w=E[i].w;
        int t=dis[u]+k-w-dis[v];
        if(t<0)continue;
        if(c[v][t])ff=1;
        f[u][k]+=dfs(v,t),f[u][k]%=P;
    }
    c[u][k]=0;
    return f[u][k];
}
void Solve(){
    spfa();
    f[1][0]=1;
    int ans=0;
    for(int j=0;j<=K;j++)
    ans+=dfs(n,j),ans%=P;
    dfs(n,K+1);
    if(ff){
        cout<<-1<<endl;
        return ;
    }
    printf("%d\n",ans);
}
int main(){
    int T=read();
    while(T--){
        Clear();
        Init();
        Solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值