L题:Dropping tests POJ-2976
- 题意:给定n个a[i]和b[i],求在去除其中k组数的情况下,找到100*∑(ai)/∑bi的最大值
- 题解:求分数和,这其实是一个01分数规划的样例题,为什么符合二分的模型也是因为可以拟合成一条曲线,所求答案成单调性,即为曲线的截距。具体操作见https://blog.csdn.net/hzoi_ztx/article/details/54898323
- 代码
#include <iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e4+100;
int n,k;
int a[N],b[N];
double d[N];
double eps=1e-5;
bool check(double x)
{
for(int i=1;i<=n;i++)
{
d[i]=a[i]-b[i]*x;
}
sort(d+1,d+1+n);
double sumall=0;
for(int i=k+1;i<=n;i++)
{
sumall+=d[i];
}
if(sumall>0)return true;
else return false;
}
int main()
{
while(cin>>n>>k&&n!=0)
{
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=n;i++)
{
cin>>b[i];
}
double l=0,r=1.0;
while(r-l>eps)
{
double mid=(l+r)/2;
if(check(mid))
{
l=mid;
}
else r=mid;
}
cout<<(int)(l*100+0.5)<<endl;
}
}
M题:K-Best POJ-3111
- 题意:给定n个财宝,价值为vi重量为wi,求在拿k个财宝的基础上,使得s=∑vi/∑wi最大
- 题解:还是个01分数规划的题啊,不同的是这个题需要保留所取的方案数,并且取的是最大的k个,开个结构体处理下就好,直接看代码吧
- 代码:
#include <iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+100;
const double eps=1e-8;
int k,n;
struct node
{
int v,w;
int i;
}nodes[N];
struct node2
{
double x;
int i;
bool operator<(const node2 &y)const
{
return x<y.x;
}
}node2s[N];
bool check(double x)
{
for(int i=1;i<=n;i++)
{
node2s[i].i=i;
node2s[i].x=nodes[i].v-x*nodes[i].w;
}
sort(node2s+1,node2s+1+n);
double sum=0;
for(int i=n;i>=n-k+1;i--)
{
sum+=node2s[i].x;
}
if(sum>0)return true;
else return false;
}
int main()
{
cin>>n>>k;
double l=0,r=1e7;
for(int i=1;i<=n;i++)
{
cin>>nodes[i].v>>nodes[i].w;
nodes[i].i=i;
}
while(r-l>eps)
{
double mid=(l+r)/2;
if(check(mid))
{
l=mid;
}
else r=mid;
}
for(int i=n;i>=n-k+1;i--)
{
cout<<node2s[i].i<<' ';
}
cout<<endl;
}
N题:Median POJ-3579
- 题意:n个数,∣Xi - Xj∣构成一个新的排序,求新的排序的中位数
- 题解:
中位数符合二分的一个规律,同时可以猜到新的中位数和原来序列的中位数存在关联。具体操作:将原来n个数排列,然后对最小数到最大数为l,r进行二分(代码里l取得0)然后check(mid)看一下有多少个比a[i]+mid要大的数,只有为m/2时,才为中位数
小于等于二分之m的话,则r=mid
大于二分之m的话,l=mid+1
注意:1.这里卡了一个输入流,关闭scanf也不行,只能直接用scanf
2.这种二分的模板下,最后答案应该是l-1,想了下,大概是中位数为(l+r)/2的时候会自动向左靠拢,这种情况下,最后答案取得是小于m/2而不是等于的值,+1就好了(不知道会不会被特判掉?感觉没啥问题)
代码:
#include <iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
const int N=1e5+10;
int n;
int a[N];
ll m;
bool check(int x)
{
ll cnt=0;
for(int i=1;i<=n;i++)
cnt+=n-(lower_bound(a+1,a+n+1,a[i]+x)-a-1);
return cnt<=m/2;
}
int main()
{
while(~scanf("%d",&n))
{
int l=0,r=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
m=n*(n-1)/2;
sort(a+1,a+1+n);
r=a[n];
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid))
{
r=mid;
}
else l=mid+1;
}
printf("%d\n",l-1);
}
}
O题:Matrix POJ-3685
- 题意:给定n,m n为n*n的方阵,其中每一个数的值为i2 + 100000 × i + j2 - 100000 × j + i × j,求方阵中的第m小的数
- 题解:
首先,想找第m个小数,对数的范围进行二分,然后判断是不是有m个数比其小,这个符合二分的定义
然后在检验有多少个数比其小的过程中,可以发现该函数在j固定的时候,是关于i的增函数,于是循环遍历j,对于每一个j我们都可以进行二分查找i的值,看能生成多少个比当前查找的mid值要小的数,如果最终遍历后小于m,则返回false,反之返回true
注意:
这个题在写第二个二分的时候要注意到l=r的时候是可以进入二分条件中的,或者可以特判一下!
代码:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
ll n,m;
int t;
ll fun(ll x,ll y)
{
return x*x+100000*x+y*y-100000*y+x*y;
}
bool check(ll x)
{
ll cnt=0;
for(ll j=1;j<=n;j++)
{
ll l=1;
ll r=n;
ll ans=0;
while(l<=r)
{
ll mid=(l+r)>>1;
if(l==0&&r==0)
{
ans=0;
break;
}
if(fun(mid,j)<=x)
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
cnt+=ans;
}
return cnt>=m;
}
int main()
{
cin>>t;
while(t--)
{
cin>>n>>m;
ll l=-1e5*n;
ll r=n*n*3+1e5*n;
while(l<r)
{
ll mid=(l+r)>>1;
if(check(mid))
{
r=mid;
}
else {
l=mid+1;
}
}
cout<<l<<endl;
}
}