题意:给n个点,第i个点坐标(xi,yi),保证 xi<xi−1 且相邻两个点的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][j−x][k]
最后用
f[i][j][k]+len[i]
更新
f[i][j+1][k+1]
考虑是否选当前点。
如果i,j都枚举到size那么复杂度可以降为 O(k∗n2)
#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;
}