NOIP2017TG D1T3 逛公园

题目链接

 

题意:

求有向图内$1$和$n$两点间路径计数,路径长度不超过最短路长度加$K$。

 

程序1(30分):

考虑记忆化搜索,设$dis_u$为点$n$到点$u$的最短路径长度,$f_{u,k}$表示$len(u,n)<=dis_u+k$的路径计数。

那么对于一条边$(u,v,w)$,一次转移中“耗费”的“额外路程”

$$len_{ex}=dis_v+w-dis_u$$

没什么好说,边取模边叠加即可。

考虑零环,发现等价于重复搜索,插旗即可。

然而神秘错误导致RE,得到30分。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#define IL inline
#define UI unsigned
using namespace std;
const int inf=2147483647;
const int N=1e5;
const int M=2e5;
const int K=50;

    int T,n,m,k,p;
    
struct Edge{
    int to,nxt,cap;
    
}e[M+3],g[M+3];
    int h[N+3],top,l[N+3],tpp;
    
void add(int u,int v,int w,Edge *e,int *h,int &top){
    top++;
    e[top].to=v;
    e[top].nxt=h[u];
    e[top].cap=w;
    h[u]=top;
    
}

struct Dat{
    int v,d;
    
    Dat(int a,int b)
        :v(a),d(b){}
    
};    

bool operator<(Dat a,Dat b){
    return a.d>b.d;
    
}

    priority_queue<Dat>que;
    int dis[N+3];

void dij(int S,Edge *e,int *h){
    for(int i=1;i<=n;i++)
        dis[i]=inf;
    dis[S]=0;
    que.push(Dat(S,0));
    
    while(!que.empty()){
        Dat x=que.top();
        que.pop();
        
        while(!que.empty()&&dis[x.v]<x.d){
            x=que.top();
            que.pop();
            
        }
        
        if(dis[x.v]<x.d)
            continue;
        
        int u=x.v;
        for(int i=h[u];~i;i=e[i].nxt){
            int v=e[i].to;
            
            if(dis[u]+e[i].cap<dis[v]){
                dis[v]=dis[u]+e[i].cap;
                que.push(Dat(v,dis[v]));
                
            }
            
        }
        
    }
    
}

    int f[N+3][K+3];
    bool vis[N+3][K+3];
    
int mod(int x){
    if(x>=p)
        return x-p;
    return x;
    
}
    
int dfs(int u,int k){
    if(vis[u][k])
        return -1;
    
    if(f[u][k]>0)
        return f[u][k];
    
    vis[u][k]=1;
    if(u==n)
        f[u][k]=1;
    else 
        f[u][k]=0;
        
    for(int i=h[u];~i;i=e[i].nxt){
        int v=e[i].to;
        int tmp=dis[v]-dis[u]+e[i].cap;
        if(tmp<=k){
            int res=dfs(v,k-tmp);
            if(res==-1){
                f[u][k]=-1;
                return -1;
                
            }
        
            f[u][k]=mod(f[u][k]+res);
            
        }
        
    }
    
    vis[u][k]=0;
    return f[u][k];
    
}

int main(){
    scanf("%d",&T);
    
    while(T--){
        scanf("%d%d%d%d",&n,&m,&k,&p);
        memset(h,-1,sizeof h);
        memset(l,-1,sizeof l);
        top=tpp=-1;
        for(int i=1;i<=m;i++){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z,e,h,top);
            add(y,x,z,g,l,tpp);
            
        }
        
        dij(n,g,l);
        
        memset(f,0,sizeof f);
        memset(vis,0,sizeof vis);
        
        printf("%d\n",dfs(1,k));
        
    }
    
    return 0;
    
}

 

 

程序2(100分):

思考RE来源,考虑越界爆栈,发现“额外路程”可能为负导致越界。

于是加了一句话,成功AC。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#define IL inline
#define UI unsigned
using namespace std;
const int inf=2147483647;
const int N=1e5;
const int M=2e5;
const int K=50;

    int T,n,m,k,p;
    
struct Edge{
    int to,nxt,cap;
    
}e[M+3],g[M+3];
    int h[N+3],top,l[N+3],tpp;
    
void add(int u,int v,int w,Edge *e,int *h,int &top){
    top++;
    e[top].to=v;
    e[top].nxt=h[u];
    e[top].cap=w;
    h[u]=top;
    
}

struct Dat{
    int v,d;
    
    Dat(int a,int b)
        :v(a),d(b){}
    
};    

bool operator<(Dat a,Dat b){
    return a.d>b.d;
    
}

    priority_queue<Dat>que;
    int dis[N+3];

void dij(int S,Edge *e,int *h){
    for(int i=1;i<=n;i++)
        dis[i]=inf;
    dis[S]=0;
    que.push(Dat(S,0));
    
    while(!que.empty()){
        Dat x=que.top();
        que.pop();
        
        while(!que.empty()&&dis[x.v]<x.d){
            x=que.top();
            que.pop();
            
        }
        
        if(dis[x.v]<x.d)
            continue;
        
        int u=x.v;
        for(int i=h[u];~i;i=e[i].nxt){
            int v=e[i].to;
            
            if(dis[u]+e[i].cap<dis[v]){
                dis[v]=dis[u]+e[i].cap;
                que.push(Dat(v,dis[v]));
                
            }
            
        }
        
    }
    
}

    int f[N+3][K+3];
    bool vis[N+3][K+3];
    
int mod(int x){
    if(x>=p)
        return x-p;
    return x;
    
}
    
int dfs(int u,int k){
    if(vis[u][k])
        return -1;
    
    if(f[u][k]>0)
        return f[u][k];
    
    vis[u][k]=1;
    if(u==n)
        f[u][k]=1;
    else 
        f[u][k]=0;
        
    for(int i=h[u];~i;i=e[i].nxt){
        int v=e[i].to;
        int tmp=dis[v]-dis[u]+e[i].cap;
        
        if(tmp<0)
            continue;
        
        if(tmp<=k){
            int res=dfs(v,k-tmp);
            if(res==-1){
                f[u][k]=-1;
                return -1;
                
            }
        
            f[u][k]=mod(f[u][k]+res);
            
        }
        
    }
    
    vis[u][k]=0;
    return f[u][k];
    
}

int main(){
    scanf("%d",&T);
    
    while(T--){
        scanf("%d%d%d%d",&n,&m,&k,&p);
        memset(h,-1,sizeof h);
        memset(l,-1,sizeof l);
        top=tpp=-1;
        for(int i=1;i<=m;i++){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z,e,h,top);
            add(y,x,z,g,l,tpp);
            
        }
        
        dij(n,g,l);
        
        memset(f,0,sizeof f);
        memset(vis,0,sizeof vis);
        
        printf("%d\n",dfs(1,k));
        
    }
    
    return 0;
    
}

 

小结:

流氓特判有点强。

 

posted @ 2019-07-09 11:05  汉谡  阅读(192)  评论(0编辑  收藏  举报