洛谷P3354 [IOI2005]Riv 河流——“承诺”DP

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

状态中要记录一个“承诺”,只需相同承诺之间相互转移即可;

然后就是树形DP的套路了。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int n,m,head[105],ct,siz[105],fa[105],len[105];
ll ed[105],f[105][55][105][3];//点,伐木场个数,前伐木场,是否有伐木场 
ll a[105],c[105][105];//c:从i到j代价 
struct N{
    int to,next;
    ll w;
    N(int t=0,int n=0,ll w=0):to(t),next(n),w(w) {}
}edge[100005];
void init(int cr,int cnt,ll dis,int nw)
{
    c[cr][cnt]=dis*a[cr];
    if(cnt)f[cr][0][cnt][0]=c[cr][cnt];
    if(!nw)
    {
        len[cr]=cnt;return;
    }
    init(cr,cnt+1,dis+ed[nw],fa[nw]);
}
void dfs(int x)
{
    siz[x]=1;
    for(int j=1;j<=n;j++)f[x][j][0][1]=0;
    for(int k=1;k<=len[x];k++)f[x][1][k][1]=0;
    for(int i=head[x],v;i;i=edge[i].next)
    {
        dfs(v=edge[i].to);
        for(int j=min(m,siz[x]+siz[v]);j>=0;j--)
        {
            for(int k=0;k<=len[x];k++)
            {
                f[x][j][k][0]+=min(f[v][0][k+1][0],f[v][0][k+1][1]);
                f[x][j][k][1]+=min(f[v][0][1][0],f[v][0][1][1]);
                for(int l=max(1,j-siz[x]);l<=j&&l<=siz[v];l++)
                {
                    f[x][j][k][0]=min(f[x][j][k][0],f[x][j-l][k][0]+min(f[v][l][k+1][0],f[v][l][k+1][1]));
                    f[x][j][k][1]=min(f[x][j][k][1],f[x][j-l][k][1]+min(f[v][l][1][0],f[v][l][1][1]));
                }
            }
        }
        siz[x]+=siz[v];
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,x;i<=n;i++)
    {
        scanf("%lld%d%lld",&a[i],&x,&ed[i]);
        edge[++ct]=N(i,head[x],ed[i]);head[x]=ct;
        fa[i]=x;
    }
    memset(f,1,sizeof f);f[0][0][0][0]=0;//!
    for(int i=1;i<=n;i++)init(i,0,0,i);
    dfs(0);
    printf("%lld",f[0][m][0][0]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Zinn/p/9139344.html