百炼2791-矩形覆盖-C语言-动态规划

时间:2022-05-23 00:28:16

感谢来自zml大佬的思路,按位记录占用状态真的是绝了。

/*****************************************
**文件名:poj-2791
**Copyright (c) 2010-2020 OrdinaryCrazy
**创建人:OrdinaryCrazy
**日期:20170726
**描述:poj-2791参考答案
**版本:1.0
******************************************/
/**
首先的一个感觉就是问题的解决思路不是很直观,推断应该是搜索算法
既然是搜索算法,就要找到问题对应的状态,
应该是这样的,我们的目标就是找到一个合适矩形组合,使得这个组合
既能覆盖所有的点,又能使得面积最小,
状态:所有的点被覆盖的情况,总面积
初始条件:所有的点都还没有被覆盖,总面积为0
目标 :所有的点都被覆盖,总面积为s
递推规则:如果我们能知到任意一种占用情况对应的尚需最小总面积,那问题顿时就简单了,
问题就解决了,我们需要一个2^15的数组记录状态还需的最小总面积,如果用数组记录状态,对应的
寻址将会变得非常麻烦,如果我们用一个n位的二进制数记录占用状态,用位运算改变状态
这样寻址就变得非常容易。
其实占用顺序很关键,要遍历所有可能
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LEN 15
int min_area[1<<MAX_LEN];
int occupion/**这个整数的位用来记录点的覆盖情况,1表示未被覆盖,0表示已被覆盖**/;
int point[15][2]/**每个点的信息**/,num/**点的个数**/;
int area(int i,int j)//特殊的函数,这个函数对于同一直线上的两点,返回的面积是距离差,这样在计算最小面积时会自动舍去重复占用的方案
{
if(point[i][0]==point[j][0]) return abs(point[i][1]-point[j][1]);
if(point[i][1]==point[j][1]) return abs(point[i][0]-point[j][0]);
return abs(point[i][1]-point[j][1])*abs(point[i][0]-point[j][0]);
}
int search(int n)/**搜索函数,传入值为正在处理的占用情况,输出值为对应占用情况尚需的最小面积**/
{
int i,j,k;
if(min_area[n]>=0)//如果这种状态对应的问题已经解决
return min_area[n];
if(n==0)
return min_area[n]=0;
min_area[n]=40000000;
for(i=0;i<num;i++)
{
if(n&(1<<i))//如果第i+1个点尚未被占用
{
for(j=0;j<num;j++)
{
if(i!=j)
{
int tmp,s;
tmp=n&(~((1<<i)|(1<<j)));
for(k=0;k<num;k++)
if((point[i][0]==point[k][0]&&point[j][1]==point[k][1])||(point[i][1]==point[k][1]&&point[j][0]==point[k][0]))
tmp&=(~(1<<k));
s=area(i,j)+search(tmp);
if(s<min_area[n])
min_area[n]=s;
}
}
}
}
return min_area[n];
}
int main()
{
int i;
scanf("%d",&num);
while(num)
{
for(i=0;i<num;i++)
scanf("%d%d",&point[i][0],&point[i][1]);
memset(min_area,-1,sizeof(min_area));
occupion=(1<<num)-1;//覆盖状态初始化,所有的点对应的位都是1
printf("%d\n",search(occupion));
scanf("%d",&num);
}
return 0;
}