NOIP 2017 Day1 题3:逛公园 最短路径+动态规划

本文详细解析了NOIP2017逛公园问题的动态规划解法,介绍了如何使用邻接表存储图并进行SPFA算法求最短路径,通过记忆化搜索来避免重复计算,同时探讨了如何检测0环并给出AC代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

摘自:http://blog.csdn.net/enjoy_pascal/article/details/78592786   写的很简短

analysis

有人说这是防AK题?不算吧……(只不过我并没有做出来顶乱用)

首先用邻接表存边跑一遍spfa,dis[i]表示1到i的最短路长度(不要和我说你不会spfa) 
f[i][j]表示从1到i的所有路径里,比dis[n]大K的路径条数 
所以有

f[i][j]=f[k][dis[i]dis[k]+jlen[i][k]]

看到这个,你以为是DP? 
记忆化搜索! 
我们从n开始倒着搜索,若dis[i]dis[k]+jlen[i][k]0当然也就不搜索了

那么我们最大的敌人——判0环呢? 
f[x][y]这个状态在一遍dfs里出现两次,那就是有0环,return就好,开个数组标记一下

finally

ans=(i=0nf[n][i])modp

他的代码。我在写。。。。。。已经通过了7组数据 ,0环还在研究

/*输入
1
5 10 0 595078320
1 2 1
2 5 1
2 1 2
5 2 1
2 3 1
3 5 1
1 5 2
4 1 1
3 1 2
4 2 1
输出 2
我的输出:4 未通过*/

#include <bits/stdc++.h>
using namespace std;
const  int MAX_N=100001;
struct edge{
    int to;
    int cost;
};
vector<edge>  G[MAX_N];
vector<edge>  ReG[MAX_N];

int dis[MAX_N];
int N,M,K,pmod;
int dp[MAX_N][51];
bool roll;

void input(){

    for(int i=0;i<MAX_N;i++)
    {
        G[i].clear();
        ReG[i].clear();
    }

    cin >> N >> M >> K >> pmod;

    for(int i=1;i<=M;i++){

        int a,b,c; cin >> a >> b>>c;
        G[a].push_back(    (edge){b,c} );
        ReG[b].push_back(  (edge){a,c} );

    }

    memset(dp, 0 ,sizeof(dp));
    dp[1][0]=1;
    fill(dis ,dis+N+1, 200000000);
    roll=false;

}


int  dfs(int v,int k){
    if(dp[v][k]!=0) return dp[v][k];
    //used[v]=true;
    for(  int j=0; j<ReG[v].size(); j++ ){
        edge e = ReG[v][j];
        int t = k -(dis[e.to] +  e.cost - dis[v] );
        if(t<0) continue;
        if( dp[e.to][t]!=0 )  roll=true;
        dp[v][k] =  ( dp[v][k]+ dfs( e.to, t)%pmod ) %pmod;
    }
    return dp[v][k];

}




void dijkstra(){
    typedef pair<int ,int> P;
    priority_queue<P, vector<P>,greater<P> >  que;

    dis[1] = 0;
    que.push( P(0,1));

    while( !que.empty()){
        P p = que.top(); que.pop();
        int v= p.second;
        if( dis[v] < p.first ) continue;
        for( int i=0 ; i<G[v].size(); i++){
            edge  e =G[v][i] ;
            if(   dis[e.to] > dis[v] + e.cost ){
                dis[e.to] =  dis[v] + e.cost;
                que.push(  P( dis[e.to] , e.to ));
            }
        }
    }
}


int main()  {
    //freopen("park.in","r",stdin);
    //freopen("park.out","w",stdout);
    int T;cin>>T;
    while(T--){
        input();
        dijkstra();
        int ans=0;
        for(int i=0;i<=K; i++){
                  ans = ( ans + dfs(N,i) ) % pmod;
        }
        //if(roll) cout << -1 <<endl;
       // else
            cout <<  ans<<endl;
    }


    return 0;
}






NOIP 2017 逛公园  动态规划!


http://blog.csdn.net/qq_38678604/article/details/78572603#t4  这一篇写得好


https://www.cnblogs.com/CQzhangyu/p/7825839.html



https://www.cnblogs.com/Dndlns/p/7895996.html 



//#define debug
#include <bits/stdc++.h>
using namespace std;
const  int MAX_N=1000001;
struct edge{
    int to;
    int cost;
};
vector<edge>  G[MAX_N];
//  vector<edge>  ReG[MAX_N];
long long  dis[MAX_N];   //存储最短路径






int N,M,K,pmod;


// bool used[MAX_N];


//int rudu[MAX_N] ; // 存储 入度的值
int dp[MAX_N][51];




vector<edge> O[MAX_N];  //为零边建立图
vector<edge> RO[MAX_N];  //零边建立方向图






void input(){


    cin >> N >> M >> K >> pmod;


    for(int i=1;i<=M;i++){


        int a,b,c; cin >> a >> b>>c;
        G[a].push_back(  (edge){b,c} );
       // rudu[b]++;


        //ReG[b].push_back( (edge){a,c});
        if( c==0) {
                O[a].push_back(  (edge){b,c} ) ;
                RO[b].push_back(  (edge){a,c} ) ;
        }


    }


    memset(dp, 0 ,sizeof(dp));


    dp[1][0]=1;


    fill(dis ,dis+N+1, 2000);


//     for(int i=1;i<=M;i++){
//
//             for( int j=0;j<G[i].size();j++)
//             {
//                   cout << i <<"  "<< G[i][j].to <<"  "<< G[i][j].cost << " " <<endl;;
//             }
//             cout << endl;
//     }
}








namespace  roll {


bool used[MAX_N];
int cmp[MAX_N];
vector<int> vs  ; //后序遍历的顶点表
bool atO[MAX_N];




void dfs(int v){
    used[v]=true;
    for(  int j=0; j<O[v].size(); j++ ){
        edge e = O[v][j];
        if( !used[e.to] ) dfs( e.to);
    }


    vs.push_back(v);


}


void rdfs(int v ,int k){


    used[v]=true;


    cmp[v]=k;


    for(  int j=0; j<RO[v].size(); j++ ){
        edge e = RO[v][j];
        if( !used[e.to] ) rdfs( e.to, k);
    }






}


//强联通分量
void  scc(){


   memset(used, false , sizeof(used) );
   //memset(cmp ,-1 ,sizeof(cmp));
   fill(cmp,cmp+N+1,-1);
   memset(atO, 0,sizeof(atO));
   vs.clear();


   for(int i=1;i<=N;i++)   {
       if( !used[i]) dfs(i);
   }
  #ifdef debug
      cout << endl << "VS::";
      for(int i=1; i<=N ;i++)
            cout << vs[i] << " " ;


  #endif




   memset(used, false , sizeof(used) );


   int t=0;
   for(int i=vs.size()-1; i>=0 ;i--){
   //for(int i=0; i<vs.size() ;i--){
        if( !used[ vs[i]] )  rdfs( vs[i], t++);
   }




   #ifdef debug
    cout << endl <<  "cmp ::"<<endl;
     for(int i=1; i<=N ;i++)
       cout << i << " :" <<cmp[i] << " "<<endl;
     cout<<endl;
    #endif




   for(int i=1;i<N;i++){


           // cout << i;
           // cout << cmp[i];
           // cout << cmp[i+1];
        if(  ( cmp[i]!=-1 ) &&  ( cmp[i+1]!=-1 )  &&  ( cmp[i] == cmp[i+1])  ) {   //泪:  !cmp[i]==-1


                atO[ i ]    =  true;
                atO[ i+1 ] =  true;
       }
   }




    #ifdef debug
    cout << endl <<  "atO ::";
     for(int i=1; i<=N ;i++)
        cout << i << ": "<<  atO[i] << " ";
     cout<<endl;
    #endif




}


}


//从一号节点Dijkstra


void dijkstra(){


    typedef pair<int ,int> P;
    priority_queue<P, vector<P>,greater<P> >  que;




    dis[1] = 0;
    que.push( P(0,1));


    while( !que.empty()){
        P p = que.top(); que.pop();
        int v= p.second;


        if( dis[v] < p.first ) continue;


        for( int i=0 ; i<G[v].size(); i++){


            edge  e =G[v][i] ;


            if(   dis[e.to] > dis[v] + e.cost ){


                dis[e.to] =  dis[v] + e.cost;


                que.push(  P( dis[e.to] , e.to ));


            }
        }
    }
 #ifdef debug
      cout<<endl;
       cout <<  "dist[]: ";
       for(int i=1 ;i<=N; i++){
           cout<< i << " : "<< dis[i] << " ;   ";
       cout << endl;


    }
#endif // debug
}


//为什么不用宽度搜索呢??
int    BFS( ){
        bool  us[MAX_N];
        memset( us, false ,sizeof(us));
        queue<int> qq;


        qq.push(1);
        us[1]=true;
        while(!qq.empty()){


            int s=qq.front();   qq.pop();




            if( roll::atO[s] ) {    return -1;         }


            for(  int j=0; j < G[s].size(); j++){
                edge e =  G[s][j];
                if(   e.cost + dis[s] - dis[e.to] > K ||  e.cost + dis[s] - dis[e.to]<0 ) continue;//泪!!
                else
                if( !us[e.to]) { qq.push(e.to) ; us[e.to] =true; };


                for(int k=0; k<=K; k++){
                    int kk = k +  e.cost + dis[s] - dis[e.to] ;
                    if(  (kk>=0) && (kk <= K) ){
                            dp[e.to][ kk ]  = (  dp[e.to][ kk ]  +  dp[s ][ k ] );
                            #ifdef debug
                              cout << "from " <<  s << "dp[" << e.to << "]"
                                   << "[" <<  kk     << "] :"
                                   <<    dp[e.to][ kk] << endl;
                            #endif // debug
                    }
                }
            }


        }


        return 1;
   }






int main()
{
    //freopen("park.in","r",stdin);
    //freopen("park.out","w",stdout);


    int t;cin>>t;


    while(t--){


        input();
        dijkstra();
        roll::scc();


        if( BFS()==-1 ) cout << -1;
        else{


            int ans = 0;
            for(int i=0;i<=K; i++){
                       // cout <<"dp[N][i]" << dp[N][i];
                ans = ( ans + dp[N][i ] ) % pmod;
            }
            cout <<  ans<<endl;
        }
    }




    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值