二分法习题报告(一)
1.饥饿的奶牛
链接: 饥饿的奶牛.
解题思路:
第一步:排序
按照右端点为第一关键字,左端点为第二关键字排序。
第二步:预处理数组p
这里是关键了!众所周知,dp的各个子结构之间是不能有关联的。我们的dp是按照区间进行dp,所以对于每一个区间k,都要计算出p[k]=q,它满足:
①0≤q<k
②选择区间k之后,区间1,2,3…q仍然可以选择
③选择区间k之后,区间q+1,q+2…k-1不能选择
我们只需要快速找到一个区间,使得该区间的右端点在k的左端点左边即可。
这样就可以在dp时,快速查询在执行“选”的决策时转移的区间。
另外这一步有一个细节:n的规模大,在寻找区间时需要二分而不能暴力寻找,否则会T掉第三个点。
第三步:dp
方程是非常简单的(前面的步骤全部都是为了dp)。每个区间只有两种决策(即选与不选)。
设dp[k]为只考虑前k个区间时最多能选到多少个点。如果选k,转移到dp[p[k]]+right[k]-left[k]+1;如果不选k,转移到dp[k-1]。
代码:
#include <iostream>
#include <algorithm>
#define sta a
#define end c//后来我才知道end是保留字,无奈
using namespace std;
long long sta[150001],end[150001],b,n,p[150001],dp[150001];
bool cmp1(int i,int mid1,int mid2)
{
return end[i]<mid1 || (end[i]==mid1 && sta[i]<mid2);
}
bool cmp2(int j,int mid1,int mid2)
{
return end[j]>mid1 || (end[j]==mid1 && sta[j]>mid2);
}
void sort(int l,int r)
{
int i=l,j=r,mid1=end[(i+j)/2],mid2=sta[(i+j)/2];
while(i<=j)
{
while(cmp1(i,mid1,mid2))i++;
while(cmp2(j,mid1,mid2))j--;
if(i<=j)
{
swap(sta[i],sta[j]);
swap(end[i],end[j]);
i++;j--;
}
}
if(l<j)sort(l,j);
if(i<r)sort(i,r);
}
int binarysearch(int l,int r,int val)
{
int mid;
while(l<r)
{
mid=l+((r+1-l)>>1);
if(end[mid]<val)l=mid;
else r=mid-1;
}
return l;
}
int main()
{
int q;
cin>>b;
for(int i=1;i<=b;i++)cin>>sta[i]>>end[i];
sort(1,b);
p[1]=0;sta[0]=0;end[0]=-1;dp[0]=0;
for(int i=2;i<=b;i++)
{
q=i-1;
q=binarysearch(0,q,sta[i]);
p[i]=q;
}
for(int i=1;i<=b;i++)dp[i]=max(dp[i-1],dp[p[i]]+end[i]-sta[i]+1);
cout<<dp[b];
return 0;
}
总结:
首先这个代码不是我写的,我写了将近一个多小时还是没有一个很好的方法能通过测试。最后还是看了网上的解题思路。虽然没写出来,好好总结一样的有收获。
binarysearch()函数:作用是,在区间0到q之间找到最大的一个j使得end[j]<sta[i],用到二分的思想,大大降低了时间复杂度。
代码的难点在我看来不在二分,首先是要想到用动态规划,其实我感觉对于动态规划学的比较好的人来说,一个不难看出来。但是对于我这种小菜鸡,还真没看出来。。。所以到了动态规划的章节要好好看看。
最后就是在我心里的重头戏了,怎么样按照右端点为第一关键字,左端点为第二关键字排序。反正我是不敢想的,能力限制了我的想象。。。。下面就来好好的解释一下别人是怎么操作的。
首先是两个函数comp1和comp2。comp1:相当于是判断小于。comp2:相当于是判断大于。这个我硬看了半天没看明白,后来自己随便举了一个栗子,手动运行了一下就明白了。。。这也算是领悟到了一个看代码的方法。
然后是sort函数,其实它就是一个就是一个快速排序。标兵是最中间的那个元素。然后comp1是小于号,comp2是大于号。这样一看就很好理解了。
这个串代码可谓是结合了动态规划,二分,快速排序为一体,怪不得我写到自闭。。。。
好了,还准备一天更完所有题目的,看样子今天只能更一题了。今天有事耽误了一早上时间。小菜鸡六级还没过,要去学学六级了。