noip2002 矩形覆盖

Description

在平面上有 n 个点(n <= 50),每个点用一对整数坐标表示。例如:当 n=4 时,4个点的坐标分别为:p1(1,1),p2(2,2),p3(3,6),P4(0,7),如图1。

这些点可以用 k 个矩形(1<=k<=4)全部覆盖,矩形的边平行于坐标轴。当 k=2 时,可用如图2的两个矩形 sl,s2 覆盖,s1,s2 面积和为 4。问题是当 n 个点坐标和 k 给出后,怎样才能使得覆盖所有点的 k 个矩形的面积之和为最小呢。约定:覆盖一个点的矩形面积为 0;覆盖平行于坐标轴直线上点的矩形面积也为0。各个矩形必须完全分开(边线与顶点也都不能重合)。 

Input

有若干组测试数据。每组的第1行有两个整数n、k,分别表示平面点数和需用来覆盖这些点的矩形数。接下来有n行,每行有两个整数xi、yi,表示点的坐标(xi, yi),(0<=xi, yi<=500)。两组数据之间空一个空行。

Output

对每组测试数据,输出满足条件的最小的矩形面积之和。 


Sample Input

4 21 12 23 60 7

Sample Output

4



思路:区间dp题,感觉还是蛮难的,dp[i][j][k]表示从第i个到第j个用了k个矩形覆盖所需要的最小面积,和能量项链有些相似。对k=1进行预处理,对k从2从小到大遍历每一个区间,再遍历区间内的每一个点。对每一个点来说,遍历每一种覆盖方法(例如k=4时,对某一个点有(1,3),(2,2),(3,1)几种覆盖方法)要么和左边的进行覆盖,要么和右边的进行覆盖,取两者中的最小值就是该点的最小覆盖面积。

#include<iostream>
#include<algorithm>
#include<cstring>
#define INF 0x3f3f3f3f
using namespace std;
int dp[100][100][5];
//dp[i][j][k]: 覆盖i到j用k个矩形的最小面积 
struct node{
	int x,y;
	friend bool operator < (node a,node b){
		if(a.x==b.x) return a.y<b.y;
		else return a.x<b.x;
	}
}a[100];

int main(){
	int n,m;
	while(cin>>n>>m)
	{
		for(int i=0;i<n;i++){
			cin>>a[i].y>>a[i].x;
		}
		sort(a,a+n)  //对所有点按横坐标进行排序,便于dp;
		memset(dp,0,sizeof(dp));
		//for(int i=0;i<n;i++) cout<<a[i].x<<' '<<a[i].y<<endl;
		for(int i=0;i<n;i++){
			for(int j=i+1;j<n;j++){
				int Max=0,Min=INF;
				for(int l=i;l<=j;l++){
					Max=max(Max,a[l].y);
					Min=min(Min,a[l].y);
				}	
				dp[i][j][1]=abs(Max-Min)*(a[j].x-a[i].x); //用一个矩形覆盖任意两个点所需要的最小面积			
			}
		}	
//		for(int i=0;i<n;i++){
//			for(int j=0;j<n;j++){
//				cout<<dp[i][j][1]<<' ';
//			}
//			cout<<endl;
//		}
		for(int i=n-1;i>=0;i--){      //从右开始遍历区间左端点
			for(int j=i+1;j<n;j++){        //从小到大遍历区间右端点
				for(int k=2;k<=m;k++){		//遍历矩形个数
					for(int l=i+1;l<j;l++){  //遍历i+1~j-1的每一个覆盖断点	
						for(int t=1;t<k;t++){   //遍历每一种覆盖方法
							if(!dp[i][j][k]) dp[i][j][k]=min(dp[i][l][t]+dp[l+1][j][k-t],dp[i][l][k-t]+dp[l+1][j][t]);
							else dp[i][j][k]=min(dp[i][j][k],min(dp[i][l][t]+dp[l+1][j][k-t],dp[i][l][k-t]+dp[l+1][j][t]));
					//		cout<<i<<' '<<j<<' '<<k<<' '<<dp[i][j][k]<<endl;
						}
					}
				}
			}
		} 
		cout<<dp[0][n-1][m]<<endl;
	}
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值