zoukankan      html  css  js  c++  java
  • 洛谷3354(IOI2005)河流——“承诺”

    题目:https://www.luogu.org/problemnew/show/P3354

    虽说是几个月前曾经讲过的题,但没有题解而自己(花了两个多小时)A了好高兴!!!

    这是一个很好的套路:“承诺”以算值。

      伐木场放在哪里对于节点的值是有影响的,所以自然的思路就是把和该节点有关的伐木场位置也压进状态里,对于不同的状态算出不同的值;

        也就是“承诺”那些伐木场会放在哪里。

      相同的承诺之间才能转移。

    这样就有了一个问题:该节点的“承诺”记录的是该节点上方的下一个伐木场在哪;但是该节点放不放伐木场对于转移也有影响。

      试图用“承诺下一个伐木场在0”表示该节点放伐木场,但是有诸多不对劲;比如根据定义,0处承诺了的话,上面就没有“下一个伐木场的位置”了,导致无法转移之类;

      然后终于想到可以再开一维状态0/1表示该节点到底放没放伐木场!这样一下就变得通顺又简单!

    树形DP的坑点:那个 j 的倒序!仔细一看转移需要用到同层的小一些的 j 的。

            还有常规的看看siz等等。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define ll long long
    using namespace std;
    const int N=105;const ll INF=0x7fffffff;
    int n,m,head[N],fa[N],xnt,siz[N],len[N];
    ll a[N],c[N][N],ed[N],dp[N][55][N][2];
    struct Edge{
        int next,to;ll w;
        Edge(int n=0,int t=0,ll w=0):next(n),to(t),w(w) {}
    }edge[N<<1];
    void init(int cr,ll dis,int cnt,int nw)
    {
        c[cr][cnt]=dis*a[cr];if(cnt)dp[cr][0][cnt][0]=c[cr][cnt];
        if(!nw)
        {
            len[cr]=cnt;return;
        }
        init(cr,dis+ed[nw],cnt+1,fa[nw]);
    }
    void dfs(int cr)
    {
        siz[cr]=1;for(int j=1;j<=n;j++)dp[cr][j][0][1]=0;
        for(int k=1;k<=len[cr];k++)dp[cr][1][k][1]=0;
        for(int i=head[cr],v;i;i=edge[i].next)
        {
            dfs(v=edge[i].to);
            for(int j=min(m,siz[cr]+siz[v]);j>=0;j--)//
            {
                for(int k=0;k<=len[cr];k++)
                {
                    dp[cr][j][k][1]+=min(dp[v][0][1][0],dp[v][0][1][1]);
                    dp[cr][j][k][0]+=min(dp[v][0][k+1][0],dp[v][0][k+1][1]);
    //                printf("dp[%d][%d][%d]=%lld dp[%d][%d][%d]=%lld
    "
    //                    ,cr,j,k,dp[cr][j][k],v,0,k+1,dp[v][0][k+1]);
                    for(int l=max(1,j-siz[cr]);l<=j&&l<=siz[v];l++)
                        dp[cr][j][k][0]=min(dp[cr][j][k][0],dp[cr][j-l][k][0]+min(dp[v][l][k+1][0],dp[v][l][k+1][1])),
                        dp[cr][j][k][1]=min(dp[cr][j][k][1],dp[cr][j-l][k][1]+min(dp[v][l][1][0],dp[v][l][1][1]));
    //                printf("dp[%d][%d][%d][0]=%lld dp[%d][%d][%d][0]=%lld
    dp[%d][%d][%d][0]=%lld dp[%d][%d][%d][1]=%lld
    "
    //                    ,cr,j,k,dp[cr][j][k][0],cr,j-l,k,dp[cr][j-l][k][0],v,l,k+1,dp[v][l][k+1][0],v,l,k+1,dp[v][l][k+1][1]),
    //                printf("dp[%d][%d][%d][1]=%lld dp[%d][%d][%d][1]=%lld
    dp[%d][%d][%d][0]=%lld dp[%d][%d][%d][1]=%lld
    
    "
    //                    ,cr,j,k,dp[cr][j][k][1],cr,j-l,k,dp[cr][j-l][k][1],v,l,k+1,dp[v][l][k+1][0],v,l,k+1,dp[v][l][k+1][1]);
                }
            }
            siz[cr]+=siz[v];
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);int x;ll z;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld%d%lld",&a[i],&x,&z);
            edge[++xnt]=Edge(head[x],i,z);head[x]=xnt;
            fa[i]=x;ed[i]=z;
        }
        memset(dp,1,sizeof dp);dp[0][0][0][0]=0;
        for(int i=1;i<=n;i++)init(i,0,0,i);
        dfs(0);
        printf("%lld",dp[0][m][0][0]);
        return 0;
    }
  • 相关阅读:
    Linux 下的类似Windows下Everything的搜索工具
    windows和linux环境下制作U盘启动盘
    程序调试手段之gdb, vxworks shell
    LeetCode 1021. Remove Outermost Parentheses (删除最外层的括号)
    LeetCode 1047. Remove All Adjacent Duplicates In String (删除字符串中的所有相邻重复项)
    LeetCode 844. Backspace String Compare (比较含退格的字符串)
    LeetCode 860. Lemonade Change (柠檬水找零)
    LeetCode 1221. Split a String in Balanced Strings (分割平衡字符串)
    LeetCode 1046. Last Stone Weight (最后一块石头的重量 )
    LeetCode 746. Min Cost Climbing Stairs (使用最小花费爬楼梯)
  • 原文地址:https://www.cnblogs.com/Narh/p/9138553.html
Copyright © 2011-2022 走看看