学长推荐的一题。他说是水题啊唉。。可是自己想了好久,还是没有想清楚DP的过程。
首先因为是想让删掉的人尽可能少所以头儿会先把差的大于一的说出来。开始竟然想起了二分匹配,我真是被网络流虐了。
排完了序之后就想到了实际上考察每个整数点就好,不必去想是哪一组。这样可以联想到记忆化搜但同时也使我后面陷入了一个误区。
本来该踢的踢掉以后还剩下500*500的状态觉得记忆化搜是可以,但是又觉得比较麻烦。
然后又开始想干脆直接贪吧。为什么会出现最小值最大的情况呢?因为他可能会先把两个数牵涉到的组数都是二的那组先说出来这样那群人就没辙了。可以发现,其实每个数排完序以后牵涉到的最多就是2组。这样就可以找到一段以牵涉组数为1开头和结尾中间牵涉组数为2的连续区间。但是到了这里发现,还是不能用贪心确定先报哪一组,所以还是要DP。
好了。到了这里,我开始的那个想法(只考虑是哪个数不考虑是哪组数)就开始让我陷入了囧境。其实这个时候还是要正常的去DP的。
而且!!非常需要注意的是,这个答案其实只和长度有关!!!
如果看到了这一点,那么dp方程就是dp[i]=max(dp[i],min(dp[j-1]+d[i-j],dp[j]+dp[i-j-1]))。
然后再注意一下初始化就好了。
这题1A的太假了……TvT
/*ZJU 3161*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
int n,m;
int dp[510],d[510];
inline void cal()
{
int i,j;
dp[0] = 0;
dp[1] = 1;
for(i = 2;i<500;i++)
{
dp[i] = 0;
for(j = 1;j<i;j++)
{
int tmp = min(dp[j-1]+dp[i-j],dp[j]+dp[i-j-1]);
dp[i] = max(dp[i],tmp);
}
}
}
int main()
{
int i,j,u,v;
cal();
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(d,0,sizeof(d));
for(i=0;i<m;i++)
{
scanf("%d%d",&u,&v);
if(abs(u-v)==1)d[u]++,d[v]++;
}
int res = 0,num = 0;
for(i = 0;i<n;i++)
{
if(d[i]==0)res++;
else if(d[i]==1&&num == 0)num = 1;
else if(d[i]==2)num++;
else if(d[i]==1&&num>0)
{
res+=dp[num+1];
num = 0;
}
}
printf("%d/n",res);
}
return 0;
}