您的位置:首页 > 其它

【DP】SRM552 PointErasing

2018-03-21 18:38 363 查看

题意:

在一个平面上,给出NN个点,现在重复进行如下操作:

1、选择两个横,纵坐标均不同的点,并且要求以这两个点为顶点的矩形,必须满足矩形内部(不含边线)存在至少1个点,若不存在,则停止。

2、删去矩形内部的所有点。

现在求剩下的点的总数的所有可能结果

N≤50N≤50

分析:

首先,很容易发现,在某维坐标为最值的点是一定不会被包含的,因此,我们选中这些点,这些点组成的矩形如下:



(注:这是样例7的图)

这样一来,结论就很清晰了:所有点都会被覆盖,但由于题目要求的是严格内部,所以红线画出的区域其实并没有被覆盖。也就是说,除了红线上的点,其它的点在任意一组方案中,最终都会被消掉。

那么我们再来找一下这条红线的严格定义:

其必然是由最左端的一个点向右,直到一个最高/最低点为止

以及由最右端的一个点向左,直到一个最高/最低点为止。

证明很简单,首先,很显然红线上的点必然和左端点/右端点在同一高度上,否则若其不是最高点,则必然会被以最高点与左端点/右端点形成的矩形覆盖。

在此基础上,若该点超过了从端点出发遇到的第一个最高/最低点,则其必然可以通过第一个最高/最低点与任意一个最低/最高点形成的矩形覆盖掉。

这样一来,我们需要考虑的范围就少了很多:左端点,右端点,最高/最低点均不能删,红线以外的点均会被删除。

现在考虑红线上的点:我们根据其是与左端点还是右端点高度相同,分成两个部分,分别处理(即左右两段红线)

这个时候,就可以通过DP来做了,定义DP[i][j]表示前i个点中,能否恰好删去j个点(1/0),具体转移和01背包基本相同。唯一不同的是,这里的物品不是隐式的:

为了表达方便,我们设A类点表示高于端点的点,B类点表示高度等于端点的点,C类点表示高度低于端点的点。

当我们访问到一个A类点时,我们查询其与所有之前的C类点之间的B类点的个数,这个个数即为“物品”的体积。仍然不明白可以看看代码。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#define SF scanf
#define PF printf
#define MAXN 55
using namespace std;
int maxh,minh,firh,lash,n;
bool dp[MAXN][MAXN],res[MAXN];
int h[MAXN],cnt;
int tag[MAXN];
vector<int> ans1,ans2,ans;
void add1(int x,int y,int sum){
for(int i=sum;i<n;i++)
dp[x][i]=dp[x][i]|dp[y][i-sum];
}
void add2(int x,int y,int sum){
for(int i=sum;i<n;i++)
dp[x][i]=dp[x][i]|dp[y][i-sum];
}
class PointErasing{
public:
vector<int> getOutcomes(vector<int> r1){
//SF("%d",&n);
n=r1.size();
for(int i=0;i<n;i++){
SF("%d",&h[i]);
h[i]=r1[i];
}
firh=h[0],lash=h[n-1];
maxh=minh=firh;
for(int i=0;i<n;i++){
maxh=max(maxh,h[i]);
minh=min(minh,h[i]);
}
if(minh==maxh){
ans.push_back(n);
return ans;
}
for(int i=0;i<n&&h[i]!=minh&&h[i]!=maxh;i++)
if(h[i]==firh){
tag[i]=1;
cnt++;
}
for(int i=n-1;i>=0&&h[i]!=minh&&h[i]!=maxh;i--)
if(h[i]==lash){
tag[i]=2;
cnt++;
}
for(int i=0;i<n;i++)
if(tag[i]==0&&(h[i]==minh||h[i]==maxh))
cnt++;

for(int i=0;i<n;i++)
dp[i][0]=1;
if(firh!=minh&&firh!=maxh){
int i=0;
for(;i<n&&h[i]!=minh&&h[i]!=maxh;i++){
if(i!=0)
for(int j=0;j<n;j++)
dp[i][j]=dp[i-1][j];
int sum=0;
if(h[i]>firh){
for(int j=i-1;j>=0;j--){
if(h[j]==firh)
sum++;
if(h[j]<firh)
add1(i,j,sum);
}
}
if(h[i]<firh){
for(int j=i-1;j>=0;j--){
if(h[j]==firh)
sum++;
if(h[j]>firh)
add1(i,j,sum);
}
}
}
for(int j=0;j<n;j++)
dp[i][j]=dp[i-1][j];
int sum=0;
for(int j=i-1;j>=0;j--){
if(h[j]==firh)
sum++;
else
add1(i,j,sum);
}
for(int j=0;j<n;j++)
if(dp[i][j]==1)
ans1.push_back(j);
}
else
ans1.push_back(0);
if(lash!=minh&&lash!=maxh){
int i=n-1;
for(;i>=0&&h[i]!=minh&&h[i]!=maxh;i--){
if(i!=n-1)
for(int j=0;j<n;j++)
dp[i][j]=dp[i+1][j];
int sum=0;
if(h[i]>lash){
for(int j=i+1;j<n;j++){
if(h[j]==lash)
sum++;
if(h[j]<lash)
add2(i,j,sum);
}
}
if(h[i]<lash){
for(int j=i+1;j<n;j++){
if(h[j]==lash)
sum++;
if(h[j]>lash)
add2(i,j,sum);
}
}
}
int sum=0;
for(int j=0;j<n;j++)
dp[i][j]=dp[i+1][j];
for(int j=i+1;j<n;j++){
if(h[j]==lash)
sum++;
else
add2(i,j,sum);
}
for(int j=0;j<n;j++)
if(dp[i][j]==1)
ans2.push_back(j);
}
else
ans2.push_back(0);
for(int i=0;i<ans1.size();i++)
for(int j=0;j<ans2.size();j++)
res[cnt-ans1[i]-ans2[j]]=1;
for(int i=0;i<=n;i++)
if(res[i])
ans.push_back(i);
return ans;
}
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: