• [NOIP2017] 逛公园 (最短路,动态规划&记忆化搜索)


    题目链接


    Solution

    我只会60分暴力...
    正解是 DP.
    状态定义:
    (f[i][j]) 代表 (1)(i) 比最短路长 (j) 的方案数.
    那么很显然最后答案也就是 (sum^{i=0}_{k}f[n][i]).

    转移方程:
    对于任一状态 (f[i][j]) 我们对可以到达它的点 (v) 进行讨论:

    1. (v) 本身为 (1)(i) 的最短路上的节点,则此时 $$f[i][j]+=f[v][j]$$
    2. (v) 并非到其最短路上的点.
      那么此时从 (v)(i) 相当于多走了((dis[i]-(dis[v]+w_{v,i})))这么长.
      所以此时 $$f[i][j]+=f[v][j-(dis[i]-(dis[v]+w_{v,i}))]$$

    然后很明显 (1) 也可以表示为 (2) 状态.
    所以 (2) 状态即为总动态转移方程.

    (0):
    由于题目中给出的图并非一张 (DAG) ,所以可能存在 (0) 环的情况.
    如果 (DP) 从前往后推,那么可以使用拓扑排序.
    记忆化搜索则需要判断一种状态是否在一次搜索时出现多次.

    此处给出记忆化搜索的代码.

    Code

    #include<bits/stdc++.h>
    #define in(x) x=read()
    #define ll long long
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int maxn=100008;
    
    struct node{int u; ll d;
        bool operator <(const node& kkk)const
        {return d>kkk.d;}
    };
    struct sj{int to,next;ll w;}a[maxn*4];
    int head[maxn],size,Head[maxn];
    ll f[maxn][52],dis[maxn],ans;
    int n,m,k,mod,c[maxn][52],ff;
    int v[maxn][52],vis[maxn];
    
    int read()
    {
    	char ch=getchar(); int f=1,w=0;
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
    	return f*w;
    }
    
    void add(int x,int y,int w)
    {a[++size].to=y;a[size].next=head[x];head[x]=size;a[size].w=w;}
    void Add(int x,int y,int w)
    {a[++size].to=y;a[size].next=Head[x];Head[x]=size;a[size].w=w;}
    
    void Dijkstra()
    {
         priority_queue<node>q;
         memset(dis,127,sizeof(dis));
         q.push((node){1,0});
         dis[1]=0;
         while(!q.empty())
         {
             node x=q.top();q.pop();
             int u=x.u;
             for(int i=head[u];i;i=a[i].next)
             {
                 int tt=a[i].to;
                 if(dis[tt]>dis[u]+a[i].w)
                 {
                     dis[tt]=dis[u]+a[i].w;
                     q.push((node){tt,dis[tt]});
                 }
             }
         }
         return;
    }
    
    ll dfs(int x,int kk)
    {
    	if(v[x][kk])return f[x][kk];
    	v[x][kk]=1;
    	c[x][kk]=1;
    	if(ff==1)return 0;
    	for(int i=Head[x];i;i=a[i].next)
    	{
    		int tt=a[i].to;
    		int t=dis[x]+kk-a[i].w-dis[tt];
    		if(t<0)continue;
    		if(c[tt][t]){ff=1;return 0;}
    		f[x][kk]+=dfs(tt,t);
    		f[x][kk]%=mod;
    	}
    	c[x][kk]=0;
    	return f[x][kk];
    }
    
    int main()
    {	
    	int t; in(t);
    	while(t--)
    	{
    		//Init 
    		memset(c,0,sizeof(c));
    		memset(v,0,sizeof(v));
    		memset(dis,127,sizeof(dis));
    		memset(Head,0,sizeof(Head));
    		memset(head,0,sizeof(head));
    		memset(a,0,sizeof(a)); size=0;
    		memset(f,0,sizeof(f)); ff=0;
    
    		//Input	
    		in(n); in(m); in(k); in(mod);
    		for(int i=1;i<=m;i++)
    		{
    			int x,y,z; in(x),in(y),in(z);
    			add(x,y,z); Add(y,x,z);
    		}		
    		Dijkstra();
    	
    		//dfs + DP
    		ans=0;
    		f[1][0]=v[1][0]=1;
    		for(int i=0;i<=k;i++)
    		{
    			dfs(n,i),ans+=f[n][i],ans%=mod;
    			if(ff==1)break;
    		}
    		dfs(n,k+1);
    		if(ff==1)
    		{printf("-1
    ");continue;}
    		
    		printf("%lld
    ",ans); 
    	}
    }
    
  • 相关阅读:
    三级联动
    投票系统
    增删改查
    PHP基础
    查询练习
    高级查询
    高级查询练习题
    0510课堂02三元运算符,跳转语句,循环结构
    0510课堂
    050602课堂css3旋转、过渡、动画
  • 原文地址:https://www.cnblogs.com/Kv-Stalin/p/9670043.html
Copyright © 2020-2023  润新知