题意给定你n个点,每个点可以停在原地或者向左向右,问最多能有多少个不重合的点 还有 最少能有多少个点。
解题报告:看题目就感觉是dp题 但听大佬说不能dp做?
但我抱着侥幸的心里写了写dp,一开始确实wa了,后来看了看状态转移 ,我们dp数组定义的是考虑前i个人并且第i个人的状态是向左、向右、不动 ,如果每个状态都能被三个状态转移的话 ,其实是有问题的,我们没有保证i-1个的坐标是比i要小于等于的,如果上一个人的坐标比i大 那么就会有问题 例如dp[i][2]还是会被dp[i][1]所更新到,所以我想把状态定义为以i为结尾,且i为最大的状态,保证前一个点<=后一个点就行了。
ac代码:
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=200010;
int x[N];
bool st[N];
int dp[N][3]; //考虑前i个人 第i个人往左不动往右 的最大房间数
int main()
{
int n;
cin>>n;
int minv=1e9;
for(int i=1;i<=n;i++)
{
cin >> x[i];
st[x[i]]=true;
minv=min(minv,x[i]);
}
sort(x+1,x+1+n);
int lf=minv;
int res_min=1e9;
// for(int i=1;i<=n;i++)
// {
// if(st[i] && i>lf+2)
// {
// res_min++;
// lf=i;
// }
// }
memset(dp,0x3f,sizeof dp);
dp[1][0]=1;
dp[1][1]=1;
dp[1][2]=1;
for(int i=2;i<=n;i++)
{
if((x[i-1]+1)<=x[i]) dp[i][0]=min(dp[i][0],dp[i-1][1]+((x[i-1]+1)<x[i]));
dp[i][0]=min(dp[i][0],dp[i-1][0]+(x[i-1]<x[i]));
dp[i][0]=min(dp[i][0],dp[i-1][2]+1);
dp[i][1]=min(dp[i][1],dp[i-1][0]+1);
dp[i][1]=min(dp[i][1],dp[i-1][1]+(x[i-1]<x[i]));
dp[i][1]=min(dp[i][1],dp[i-1][2]+1);
dp[i][2]=min(dp[i][2],dp[i-1][2]+(x[i-1]<x[i]));
if(x[i-1]+1<=x[i]-1)
dp[i][2]=min(dp[i][2],dp[i-1][1]+(x[i-1]<x[i]-2));
if(x[i-1]<=x[i]-1)
dp[i][2]=min(dp[i][2],dp[i-1][0]+(x[i-1]<(x[i]-1)));
}
res_min=min(dp[n][2],min(dp[n][0],dp[n][1]));
memset(dp,0,sizeof dp);
dp[1][0]=1;
dp[1][1]=1;
dp[1][2]=1;
for(int i=2;i<=n;i++)
{
if((x[i-1]+1)<=x[i]) dp[i][0]=max(dp[i][0],dp[i-1][1]+((x[i-1]+1)<x[i]));
dp[i][0]=max(dp[i][0],dp[i-1][0]+(x[i-1]<x[i]));
dp[i][0]=max(dp[i][0],dp[i-1][2]+1);
dp[i][1]=max(dp[i][1],dp[i-1][0]+1);
dp[i][1]=max(dp[i][1],dp[i-1][1]+(x[i-1]<x[i]));
dp[i][1]=max(dp[i][1],dp[i-1][2]+1);
dp[i][2]=max(dp[i][2],dp[i-1][2]+(x[i-1]<x[i]));
if(x[i-1]+1<=x[i]-1)
dp[i][2]=max(dp[i][2],dp[i-1][1]+(x[i-1]<x[i]-2));
if(x[i-1]<=x[i]-1)
dp[i][2]=max(dp[i][2],dp[i-1][0]+(x[i-1]<(x[i]-1)));
}
int res=max(dp[n][0],max(dp[n][1],dp[n][2]));
cout<<res_min<<' '<<res<<endl;
return 0;
}