题目:http://www.lydsy.com/JudgeOnline/problem.php?id=4254
题意:按照规则选m条线段,使得不超过k限制,得到权值和最大。
题解:按照给定的规则,一条合法的线段下面必须全部低于此线段,也就是每一个线段一定被某个线段的完全覆盖,且不会出现线段非重合相交的情况。故所有的线段依据覆盖关系可形成一个森林,创建虚拟节点0,使得构建一棵树。然后就是树形dp,dfs过程中要套一个背包。其实最重要的是转换出这个模型。
f[i][d][j] 表示在i节点选取d个线段,重叠最大次数不超过j的最优方案。转移过程就是对i的儿子背包,注意要做两次背包,一次是不选i这个线段,另一个是必须选i。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 510;
const int inf = 1e8;
typedef long long ll;
int f[N][N][15],val[N];
struct point{
int x,y;
}p[N];
int cmp1(point a,point b){
return a.x < b.x;
}
struct line{
int x1,x2,y;
line(){}
line(int _x1,int _x2,int _y){
x1 = _x1; x2 = _x2; y = _y;
}
}le[N];
int cmp2(line a,line b){
return abs(a.x2-a.x1) < abs(b.x2-b.x1);
}
int n,m,k,num;
vector<int> g[N];
vector<int> son[N];
void make_line(){
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++){
if(p[i].y==p[j].y){
int flag = 1;
for(int d=i+1;d<j;d++) if(p[d].y>=p[i].y) flag=0;
if(!flag) continue;
le[++num] = line(p[i].x,p[j].x,p[i].y);
break;
}
}
}
void tro_line(){
for(int i=1;i<=num;i++){
int flag = 1;
for(int j=i+1;j<=num;j++)if(le[i].y < le[j].y){
if(le[i].x1>le[j].x1 && le[i].x2<le[j].x2){
flag = 0;
g[j].push_back(i);
break;
}
}
if(flag){
g[0].push_back(i);
}
}
}
int h[N][N],v[N];
int dfs(int x,int pa){
son[x].clear();
int size = 1; int co =0 ;
for(int i=0;i<g[x].size();i++)if(g[x][i]!=pa){
int u = g[x][i];
son[x].push_back(u);
co++;
v[u] = dfs(u,x);
size += v[u];
}
for(int j=0;j<k;j++){
for(int i=0;i<=m;i++) h[0][i]=-inf;
h[0][0] = 0;
for(int i=1;i<=co;i++){
int u = son[x][i-1];
for(int d=0;d<=m;d++) h[i][d] = h[i-1][d];
for(int t=0;t<=v[u];t++)
for(int d=t;d<=min(size,m);d++)
h[i][d] = max(h[i][d] , h[i-1][d-t]+f[u][t][j]);
}
for(int i=0;i<=m;i++)
f[x][i][j] = max(f[x][i][j],h[co][i]);
}
if(x!=0)
for(int j=1;j<k;j++){
for(int i=0;i<=m;i++) h[0][i]=-inf;
h[0][1] = val[x];
for(int i=1;i<=co;i++){
int u = son[x][i-1];
for(int d=0;d<=m;d++) h[i][d] = h[i-1][d];
for(int t=0;t<=v[u];t++)
for(int d=t+1;d<=min(size,m);d++)
h[i][d] = max(h[i][d] , h[i-1][d-t]+f[u][t][j-1]);
}
for(int i=0;i<=m;i++)
f[x][i][j] = max(f[x][i][j],h[co][i]);
}
for(int i=1;i<=m;i++)
f[x][i][0]=-inf;
f[x][0][0]=0;
for(int i=0;i<=m;i++)
for(int j=1;j<k;j++)
f[x][i][j] = max(f[x][i][j],f[x][i][j-1]);
return size;
}
int main(){
int cas=0;
while(scanf("%d%d%d",&n,&m,&k)!=EOF){
for(int i=1;i<=n;i++){
scanf("%d%d",&p[i].x,&p[i].y);
}
num=0;
sort(p+1,p+1+n,cmp1);
make_line();
for(int i=0;i<=num+1;i++) g[i].clear();
sort(le+1,le+1+num,cmp2);
tro_line();
val[0]=-inf;
for(int i=1;i<=num;i++) val[i] = abs(le[i].x2-le[i].x1);
for(int i=0;i<=num;i++)for(int d=0;d<=m;d++)for(int j=0;j<=k;j++)f[i][d][j]=-inf;
dfs(0,-1);
printf("Case %d: %d\n",++cas,(f[0][m][k-1]>0?f[0][m][k-1]:-1) );
}
return 0;
}