bzoj 4254 树形dp

题意:给n个点,第i个点坐标(xi,yi),保证 xi<xi1 且相邻两个点的y坐标不同。对于两个y坐标相等的点,如果两个点中间所有点的y坐标小于两个点的y坐标,那么两个点可以连一条边。给出m,k。求连m条边,使每个点上方的边数小于k的最大边长和,无解输-1。

每个点最多向右连出一条边。两条边的位置关系只可能是包含或没有交集。
那么这就是很多棵树。

建出树,树形dp,设 f[i][j][k] 表示到点i,点i的子树中有j条选择的边,最大深度为k的最大边长和。
将k一维求一个前缀最大值,转移: f[i][j][k]=f[u][x][k]+f[i][jx][k]
最后用 f[i][j][k]+len[i] 更新 f[i][j+1][k+1] 考虑是否选当前点。

如果i,j都枚举到size那么复杂度可以降为 O(kn2)

#include <bits/stdc++.h>
using namespace std;
#define N 210
int n,m,K,top,Case;
int X[N],Y[N],st[N],pos[N],fa[N],len[N],size[N];
int f[N][N][11],t[N];
vector<int>vec[N];
void dfs(int x)
{
    for(int i=0;i<=K;i++)f[x][0][i]=0;
    size[x]=1;
    for(int i=0,u;i<vec[x].size();i++)
    {
        dfs(u=vec[x][i]);
        size[x]+=size[u];
        for(int j=min(m,size[x]);j>=0;j--)
            for(int k=0;k<=K;k++)
                for(int t=0;t<=size[u]&&t<=j;t++)
                {   
                    if(f[x][j-t][k]!=-1&&f[u][t][k]!=-1)
                        f[x][j][k]=max(f[x][j][k],f[u][t][k]+f[x][j-t][k]);
                }
    }
    for(int i=min(m,size[x])-1;i>=0;i--)
        for(int j=0;j<K;j++)
            if(f[x][i][j]!=-1)
                f[x][i+1][j+1]=max(f[x][i+1][j+1],f[x][i][j]+len[x]);

    for(int i=1;i<=K;i++)
        for(int j=0;j<=m&&j<=size[x];j++)
            f[x][j][i]=max(f[x][j][i],f[x][j][i-1]);
}
int main()
{
    //freopen("tt.in","r",stdin);
    while(scanf("%d%d%d",&n,&m,&K)!=EOF)
    {
        K--;
        memset(f,-1,sizeof(f));
        memset(t,-1,sizeof(t));
        memset(size,0,sizeof(size));
        for(int i=1;i<=n;i++)
            scanf("%d%d",&X[i],&Y[i]);
        top=0;
        for(int i=1;i<=n;i++)
        {
            pos[i]=fa[i]=0;
            vec[i].clear();
        }
        for(int i=1;i<=n;i++)
        {
            while(Y[i]>Y[st[top]]&&top)top--;
            if(Y[i]==Y[st[top]]&&top)
            {   
                pos[st[top]]=i;
                len[st[top]]=X[i]-X[st[top]];
                top--;
            }
            st[++top]=i;
        }
        for(int i=1;i<=n;i++)
            if(pos[i])
            {
                int p=0;
                for(int j=1;j<i;j++)
                    if(pos[j]&&X[pos[j]]>X[pos[i]])p=j;
                if(p)vec[p].push_back(i);
                fa[i]=p;
            }
        t[0]=0;
        for(int i=1;i<=n;i++)
            if(!fa[i]&&pos[i])
            {   
                dfs(i);
                for(int j=m;j>=0;j--)
                    for(int k=0;k<=size[i]&&k<=j;k++)
                        if(t[j-k]!=-1&&f[i][k][K]!=-1)
                            t[j]=max(t[j],t[j-k]+f[i][k][K]);
            }
        printf("Case %d: %d\n",++Case,t[m]);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值