zoukankan      html  css  js  c++  java
  • noip2017D1T3逛公园(拓扑图上dp,记忆化搜索)

    QWQ前几天才刚刚把这个D1T3写完

    看着题解理解了很久,果然我还是太菜了QAQ

    题目大意就是

    给你一个n个点,m条边的图,保证1能到达n,求从1到n的 (设1到n的最短路长度是d)路径长度在[d,d+k]之间的路径有多少条,答案要对p取膜

    下面附上数据范围的大表哥!

    首先对于30%的数据,我们可以直接跑最短路计数来实现QWQ

    这里最短路计数就不作详细解释了!

    一定注意的是 当更新dis[to[i]]时,要记得把ans[to[i]]赋值成ans[x] 千万不要手残写成1!!!

    上代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    #define pa pair<int,int>
    using namespace std;
    
    const int maxn = 100010;
    const int maxm = 400010;
    
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while (!isdigit(ch)){ if (ch=='-') f=-1;ch=getchar();
        }while (isdigit(ch)){ x=(x << 3)+(x << 1) + ch-'0';ch=getchar();
        }return x*f; 
    }
    
    struct Node{
        int num;
        int id;
    };
    
    priority_queue <pa,vector<pa>,greater<pa> > q;
    int point[maxn],nxt[maxm],to[maxm],val[maxm];
    int dis[maxn];
    Node qq[1000100];
    int tt[maxn],vis[maxn];
    int aa[maxn];
    int cnt,ans;
    int n,m;
    
    void addedge(int x,int y,int w)
    {
        nxt[++cnt]=point[x];
        to[cnt]=y;
        val[cnt]=w;
        point[x]=cnt;
    }
    
    void dijkstra(int s,int pp)
    {
        for (int i=1;i<=n;i++)
          dis[i]=2e9;
        memset(aa,0,sizeof(aa));
        aa[s]=1;
        dis[s]=0;
        memset(vis,0,sizeof(vis));
        q.push(make_pair(0,s));
        while (!q.empty())
        {
            int  x = q.top().second;
            q.pop();
            if (vis[x]) continue;
            vis[x]=1;
            for (int i=point[x];i;i=nxt[i])
            {
                int p=to[i];
                if (dis[p]>dis[x]+val[i])
                {
                    dis[p]=dis[x]+val[i];
                    aa[p]=aa[x]%pp;
                    q.push(make_pair(dis[p],p));
                }
                else
                  if (dis[p]==dis[x]+val[i])
                  {
                  	aa[p]=(aa[p]+aa[x])%pp;
                  }
            }
        }
    }
    
    int t,k,p;
    
    void bfs(int d,int pp)
    {
        int head=0,tail=1;
        qq[tail].id=1;
        while (head<=tail)
        {
            head++;
            int x=qq[head].id;
            for (int i=point[x];i;i=nxt[i])
            {
                int p=to[i];
                if (tt[p]>n) continue;
                qq[++tail].id=p;
                qq[tail].num=qq[head].num+val[i];
                tt[p]++;
                if (p==n&&qq[tail].num<=d+k)
                {
                    ans=(ans+1)%pp;
                }
            }
        }
    }
    
    
    int main()
    {
        t=read();
        while (t--)
        {
            scanf("%d%d%d%d",&n,&m,&k,&p);
            cnt=0;
            ans=0;
            memset(point,0,sizeof(point));
            memset(qq,0,sizeof(qq));
            memset(tt,0,sizeof(tt));
            for (int i=1;i<=m;i++)
            {
                 int u,v,w;
                 u=read();v=read();w=read();
                 addedge(u,v,w);
            }
            dijkstra(1,p);
            printf("%d
    ",aa[n]);
        }
    }
    

    这是(修改后的)考场源代码QWQ可能有点丑陋

    而对于其他数据QAQ emmmmmm

    这个嘛~

    我们就需要考虑dp

    我这里用的dp状态是

    f[i][j]表示从1到i这个点,比最短路长了j的方案数

    对于一条边 u - > v QAQ我们不难发现

    f[v][dis[u]+k+val[i]-dis[v]]+=f[u][k]; (0<=dis[u]+k+val[i]-dis[v]<=k)

    好啦!这不就可以转移了嘛?

    别急QWQ 貌似还有0环的问题。

    这里就需要思考一下0环的性质

    如果有0环的话.....这些边应该一定会出现在最短路图上吧,那么我们只需要在最短路图上跑拓扑排序~如果到最后发现无法构成DAG 那么应该就是有0环

    同时拓扑排序也是为了在最短路上的点在后面的dp中,制定一个顺序

    例如x->y->z 更新顺序一定是x y z

    而对于0环上的点,如果dis[i]+disn[i](到n的最短路)<=dis[n]+k 那么它就可以无限制的更新下去(可以理解为一直在0环上,从而使方案数变为无限)

    如果遇到这种情况 就直接输出-1了

    下面dp的部分也没什么好说的了

    枚举这个偏移量(就是比最短路长多少)

    就是分成两部分,先更新最短路的点,然后再用当前的偏移量的u,去更新更大偏移量的v

    上代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #define pa pair < int , int >
    using namespace std;
    
    inline int read()
    {
       int x=0,f=1;char ch=getchar();
       while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
       while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
       return x*f;
    }
    
    //f[to[i]][dis[u]+k+w-dis[v]]+=f[u][k]
    
    const int maxn =  1e5+1e2;
    const int maxm = 5e5+1e2;
    const int inf = 1e9;
    
    int f[maxn][60];
    int nxt[maxm],to[maxm],point[maxn],val[maxm];
    queue<int> que;
    int x[maxm],y[maxm],w[maxm];
    int vis[maxn],dis[maxn],disn[maxn];
    int in[maxn];
    int n,m,k,p;
    bool flag;
    int cnt=0;
    priority_queue< pa , vector<pa>,greater<pa> > q;
    int mod;
    
    
    void addedge(int x,int y,int w)
    {
        nxt[++cnt]=point[x];
        to[cnt]=y;
        val[cnt]=w;
        point[x]=cnt;
    }
    
    void init()
    {
       cnt=0;
       memset(point,0,sizeof(point));
       memset(f,0,sizeof(f));
       memset(in,0,sizeof(in));
       flag=true;
    }
    
    int dijkstra(int s)
    {
       memset(vis,0,sizeof(vis));
       for (register int i=1;i<=n;i++) dis[i]=inf;
       q.push(make_pair(0,s));
       dis[s]=0;
       while (!q.empty())
       {
       	  int x = q.top().second;
       	  q.pop();
       	  if (vis[x]) continue;
       	  vis[x]=1;
       	  for (register int i=point[x];i;i=nxt[i])
       	  {
       	     int p = to[i];
       	     if (dis[p]>dis[x]+val[i])
       	     {
       	     	dis[p]=dis[x]+val[i];
       	     	q.push(make_pair(dis[p],p));
       	     }
       	  }
       }
    }
    
    int dijkstran(int s)
    {
      
       memset(vis,0,sizeof(vis));
       for (register int i=1;i<=n;i++) disn[i]=inf; 
       disn[s]=0;
       q.push(make_pair(0,s));
       while (!q.empty())
       {
       	  int x = q.top().second;
       	  q.pop();
       	  if (vis[x]) continue;
       	  vis[x]=1;
       	  for (register int i=point[x];i;i=nxt[i])
       	  {
       	     int p = to[i];
       	     if (disn[p]>disn[x]+val[i])
       	     {
       	     	disn[p]=disn[x]+val[i];
       	     	q.push(make_pair(disn[p],p));
       	     }
       	  }
       }
    }
    
    int t;
    int top[maxn];
    int tmp;
    
    void tpsort()
    {
        tmp=0;
        for (register int x=1;x<=n;++x)
          for (register int i=point[x];i;i=nxt[i])
            {
            	int p = to[i];
            	if (dis[p]==dis[x]+val[i]) in[p]++;
            }
        for (register int i=1;i<=n;++i)
        if (in[i]==0) que.push(i),top[++tmp]=i;
        while (!que.empty())
        {
            int x = que.front();
            que.pop();
            for (register int i=point[x];i;i=nxt[i])
            {
                int p = to[i];
                if (dis[p]==dis[x]+val[i])
                {
                in[p]--;
                if (in[p]==0)
                {
                    que.push(p);
                    top[++tmp]=p; 
                }
                }
            }
        }
    }
    
    void dp() //之所以要拓扑排序,是因为在更新最短路上的点的时候,有一个先后顺序 就好比是u->v 必须先算u,再算v 
    {
        f[1][0]=1;
        for (register int i=0;i<=k;i++)
        {
              //更新最短路上的点 
             // cout<<1<<endl;
             for (register int j=1;j<=tmp;++j)
             {
             	int x=top[j];
             	for (register int ii=point[x];ii;ii=nxt[ii])
             	  {
             	  	int p= to[ii];
             	  	if (dis[p]==dis[x]+val[ii]) f[p][i]=(f[x][i]+f[p][i])%mod;
             	  }
             }
             for (register int x=1;x<=n;++x)
               for (register int ii=point[x];ii;ii=nxt[ii])
             	  {
             	  	int p= to[ii];
             	  	int now = dis[x]+val[ii]+i-dis[p];
             	  	if (dis[p]!=dis[x]+val[ii] && now<=k) f[p][now]=(f[x][i]+f[p][now])%mod; 
             	  }
        }
    //	cout<<2<<endl;
    }
    
    int main()
    {
      cin>>t;
      while (t--)
      {
      	 init();
      	 n=read();m=read();k=read();mod=read();
      	 for (register int i=1;i<=m;++i)
      	 {
      	 	x[i]=read();
      	 	y[i]=read();
      	 	w[i]=read();  	 
         }
         for (register int i=1;i<=m;++i)
         addedge(y[i],x[i],w[i]);
         dijkstran(n);
         init();
         for (register int i=1;i<=m;++i)
         addedge(x[i],y[i],w[i]);
         dijkstra(1);
         //for (int i=1;i<=n;i++)
          // cout<<dis[i]<<" ";
         //cout<<endl;
         //for (int i=1;i<=n;i++)
         ////  cout<<disn[i]<<" ";
         //cout<<endl;
         tpsort();
         for (register int i=1;i<=n;++i)
           if (in[i]>0 && dis[i]+disn[i]<=dis[n]+k) 
           {
           	 printf("-1
    ");
           	 flag=false;
           	 break;
           }
         if (!flag) continue;
         dp();
         int ans=0;
         for (register int i=0;i<=k;++i)
           ans=(ans+f[n][i])%mod;
         printf("%d
    ",ans);
      }
      return 0;
    }
    

    QAQ这种写法,代码常数特别大,需要卡常,才能A掉

    QWQ

    这个题的另一个做法,记忆化搜索

    正着建图求好dis后,

    然后反向建图,将f[1][0]=1

    从n开始做记忆化搜索

    f[x][k]=f[x][k]+f[to[i]][k-(dis[to[i]]+val[i]-dis[x])]
    

    一定一定一定一定注意!!!!!!!

    这种方法要将f数组初始化成-1

    在dfs 的时候

    用一个中间变量保存f的值

    最后再赋值,详情看代码吧QWQ被这个点坑了很久

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    
    using namespace std;
    
    inline int read()
    {
      int x=0,f=1;char ch=getchar();
      while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
      while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
      return x*f;
    }
    
    
    const int maxn = 2e5+1e2;
    const int maxm = 1e6+1e2;
    
    int point[maxn],nxt[maxm],to[maxm],val[maxm];
    int dis[maxn],vis[maxn];
    int f[maxn][61];
    int n,m,k,mod;
    int x[maxm],y[maxm],w[maxm];
    queue<int> q;
    int g[maxn][61];
    bool flag;
    int kk;
    int cnt;
    
    void addedge(int x,int y,int w)
    {
    	nxt[++cnt]=point[x];
    	to[cnt]=y;
    	val[cnt]=w;
    	point[x]=cnt;
    } 
    
    void init()
    {
    	cnt=0;
    	flag=true;
    	memset(point,0,sizeof(point));
    	memset(f,-1,sizeof(f));
    	memset(g,0,sizeof(g));
    }
    
    int spfa(int s)
    {
    	memset(vis,0,sizeof(vis));
    	memset(dis,127/3,sizeof(dis));
    	vis[s]=1;
    	dis[s]=0;
    	q.push(s);
    	while (!q.empty())
    	{
    		int x = q.front();
    	    q.pop();
    		vis[x]=0;
    		for (int i=point[x];i;i=nxt[i])
    		{
    			int p = to[i];
    			if (dis[p]>dis[x]+val[i])
    			{
    				dis[p]=dis[x]+val[i];
    				if (!vis[p])
    				{
    					vis[p]=1;
    					q.push(p);
    				}
    			}
    		}
    	}
    
    }
    
    int dfs(int x,int k)
    {
    	//cout<<1<<endl;
    	int ret=0;
    	if (g[x][k]) {
    		flag=false;
    		return 0;
    	}
    	if (f[x][k]!=-1) return f[x][k]; //这里如果写成if (f[x][k]) 会re 因为f[x][k]==0的状态有很多 
    	g[x][k]=1;
    	if (!flag) return 0;
    	for (int i=point[x];i;i=nxt[i])
    	{
    		int p = to[i];
    		int cnt=k-(dis[p]+val[i]-dis[x]);
    		if (cnt<0 || cnt>kk) continue;
    		  ret=(ret+dfs(p,cnt))%mod;
    		if (!flag) return 0;
    	}
    	if (!flag) return 0;
    	g[x][k]=0;
    	if (x==1 && k==0) 
    	{
    	  f[x][k]=1;
        }
        else
         f[x][k]=ret;
    	return f[x][k];
    }
    
    int t;
    
    int main()
    {
      cin>>t;
      while (t--)
      {
         init();
         n=read(),m=read(),k=read(),mod=read();
         for (int i=1;i<=m;i++)
         {
         	x[i]=read();
         	y[i]=read();
         	w[i]=read();
         	addedge(x[i],y[i],w[i]);
    	 }
    	 spfa(1);
    	 //for (int i=1;i<=n;i++) cout<<dis[i]<<" ";
    	// cout<<endl;
    	 init();
    	 for (int i=1;i<=m;i++) addedge(y[i],x[i],w[i]);
    	 int ans=0;
    	 kk=k;
    	 for (int i=0;i<=k;i++)
    	 {
    	 	//memset(g,0,sizeof(g));
    	 	int tmp = dfs(n,i)%mod;
    	 	if (!flag) break;
    	 	ans=(ans+tmp)%mod;
    	 }
    	 
    	 if (!flag) {
    	 	cout<<-1<<endl;
    	 	continue;
    	 }
    	 else
    	 {
    	 	printf("%d
    ",ans);
    	 }
      }  
      return 0;
    }
    
  • 相关阅读:
    hdu 1312 ( Red and Black )
    hdu 1429 ( 胜利大逃亡(续) )
    zjut 小X的苹果
    hdu 1253 ( 胜利大逃亡 )
    许多事
    1198 ( Farm Irrigation )
    hdu 1241 Oil Deposits
    hdu 1242 ( Rescue )
    hdu 1240 ( Asteroids! )
    zoj2966 build the electric system
  • 原文地址:https://www.cnblogs.com/yimmortal/p/10160652.html
Copyright © 2011-2022 走看看