DAG模型

有向无环图(DAG,Directed Acyclic Graph)

  • 嵌套矩形 链接
    最长路及其字典序 
    如何求DAG中不固定起点的最长路径呢
    设d(i)表示从节点i出发的最长路长度,转移方程:  d(i)  = max{d(j)+1|(i,j)∈E  }   
  • int dp(int i)
    {
        int& ans=d[i];//为表项d[i]声明一个引用ans,这样任何对ans的读写实际上都是在对d[i]进行
        if(ans>0) return ans;
        ans=1;
        for(int j=1;j<=n;j++)
        {
            if(G[i][j]) ans=ans>(dp(j)+1)?ans:dp(j)+1;
        }
        return ans;
    }
    
    
    //为了是字典序最小
    void print_ans(int i)
    {
        printf("%d ",i);
        for(int j=1;j<=n;j++) if(G[i][j]&&d[i]==d[j]+1)
        {
            print_ans(j);
            break;
        }
    }
    
  • 硬币问题 
    固定终点的最长路和最短路
    d(i) 的确切含义变为“从节点i出发到节点0的最长路径长度”
    //求最长路的代码---------下面代码有些问题
    int dp(int S)
    {
        int& ans=d[S];
        if(ans>=0) return ans;
        ans=0;
        for(int i=1;i<=n;i++) if(S>=V[i]) ans=ans>(dp(S-V[i])+1)?ans:(dp(S-V[i])+1);
        return ans;
    }
    
    //修改后---方法1
    int dp(int S)
    {
        int& ans=d[S];
        if(ans!=-1) return ans;
        ans=-1<<30;
        for(int i=1;i<=n;i++) if(S>=V[i]) ans=max(ans,dp(S-V[i])+1);
        return ans;
    }
    //方法2
    int dp(int S)
    {
        if(vis[S]) return d[S];//vis[i]表示状态i是否被访问过
        vis[S]=1;
        int& ans=d[S];
        ans=-1<<30;
        for(int i=1;i<=n;i++) if(S>=V[i]) ans=max(ans,dp(S-V[i])+1);
        return ans;
    }

    求最小,最大的两个值
     

    min[0]=max[0]=0;
    for(int i=1;i<=S;i++)
    {
        min[i]=INF;max[i]=-INF;
    }
    for(int i=1;i<=S;i++)
        for(int j=1;j<=n;j++)
        if(i>=V[j])
        {
            min[i]=min[i]<min[i-V[j]]+1?min[i]:min[i-V[j]]+1;
            max[i]=max[i]>max[i-V[j]]+1?max[i]:max[i-V[j]]+1;
        }
    printf("%d %d\n",min[S],max[S]);
      

    如何输出字典序最小的方案呢?

    void print_ans(int* d,int S)  
    {
        for(int i=1;i<=n;i++)
            if(S>=V[i]&&d[S]==d[S-V[i]]+1z)
            {
                printf("%d ",i);
                print_ans(d,S-V[i]);
                break;
            }
        
    }

    不少人喜欢用另外一种打印方法:递推时直接用min_coin[S]记录满足min[S]==min[S-V[j]]+1的最小的i,则打印省去print_ans函数中的循环,并方便地把递归写成迭代

    for(int i=1;i<=S;i++)
        for(int j=1;j<=n;j++)
        if(i>=V[j])
        {
            //注意判断中用的是“>"和”<",原因在于“字典序最小解”要求当min/max值相同时取最小的i值。
            //反过来,如果j是从大到小枚举的,就需要把“>"和“<"改成”>="和“<="才能求出字典序最小解
            if(min[i]>min[i-V[j]]+1)
            {
                min[i]=min[i-V[j]]+1;
                min_coin[i]=j;
            }
            if(max[i]<max[i-V[j]]+1)
            {
                max[i]=max[i-V[j]]+1;
                max_coin[i]=j;
            }
        }
    //只需调用print_ans(min_coin,S)和print_ans(max_coin,S)即可
    void print_ans(int* d,int S)
    {
        while(S)
        {
            printf("%d ",d[S]);
            S-=V[d[S]];
        }
    }
    


     

猜你喜欢

转载自blog.csdn.net/weixin_42373330/article/details/82817628
DAG