挑战程序设计竞赛上这部分讲的很好~我自己做连poj 3273暴力都不知道怎么暴力orz
1、假定一个解并判断是否可行
给n根绳子,长度为Li,现在想把它们剪成k根相同的绳子,问最大每根绳子的长度是多少?
:可以得到K条长度为x的绳子
总和是否大于等于K
给N个半径不同的派,分给f+1个馅饼,每个馅饼大小相同,并且要保证馅饼是从一块派上切下来的一块
:可以得到
个馅饼
//二分法查找求解单调函数
#include<iostream>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<stdio.h>
using namespace std;
const int N=1e4+10;
const double pi=acos(-1.0);
double s[N];
int n,f;
double bisort(int n) //馅饼的个数
{
double l=0;
double r=s[n-1]; //每块馅饼面积上下界
while((r-l)>1e-6)
{
int piece=0;
double mid=(l+r)/2;
//cout<<mid<<' ';
for(int i=0;i<n;++i)
piece+=s[i]/mid;
// cout<<piece<<endl;
if(piece<f+1) r=mid; //块数不够,要减少面积
else if(piece>=f+1) l=mid; //块数够,可以尝试增大面积
}
return l;
}
int main()
{
int T;
cin>>T;
while(T--)
{
memset(s,0,sizeof(s));
cin>>n>>f;
for(int i=0;i<n;++i)
{
int r;
cin>>r;
s[i]=pi*r*r;
//s[i]=r*r;
}
sort(s,s+n);
/*for(int i=0;i<n;++i)
cout<<s[i]<<endl;*/
printf("%.4f\n",bisort(n));
}
return 0;
}
poj 1905 Expanding rods 简单的二分,只要找到目标的关系即可 第一次因为精度while(ub-lb>eps) 精度不够而wa
一根直木棍的两端被墙固定,受热的时候会弯曲改变长度,也会形成一个弧,问中心移动了多少。
设圆心角的一半为x,半径为a,则弧(木棒受热后长度)与弦(木棒原长)之间的比值为x/sinx,这在0-pi/2一个单调函数
//A了
#include<stdio.h>
#include<math.h>
const double pi=acos(-1);
double l,n,c;
int main()
{
while(scanf("%lf%lf%lf",&l,&n,&c)!=EOF)
{
if(l==-1&&n==-1&&c==-1)
break;
if(n==0) {printf("0.000\n");continue;}
double coff=1+n*c;
double lb=0,ub=pi/2; //
double mid;
while(ub-lb>1e-15) //一开始是1e-10wa了,刺激
{
mid=(lb+ub)/2;
double cal=mid/sin(mid); //在0-π/2单调递增函数
if(cal>=coff) ub=mid;
else lb=mid;
}
double x=l*(1-cos(mid))/(2*sin(mid));
printf("%.3f\n",x);
}
return 0;
}
注意点:我是先做3122再做1064的,两题很像,但是1064遇到了a不了的问题
(1)输出r而不是l
(2)输出用“lf”会错,要用“f”
(3)3122和1064是double型的,和int型的不一样,l=mid而不是mid+1
(4)很迷的一点是我本来想判断一下judge(ans)是否等于k,不是的话就直接输出0.00,但是删掉这句就A了,望赐教
//poj 1064
int judge(double l)
{
int cnt=0;
for(int i=1;i<=n;++i)
cnt+=(int)(len[i]/l); //忘记置为整数了
return cnt;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)
scanf("%lf",&len[i]);
double l=0,r=1e7;
while((r-l)>1e-5)
{
double mid=(l+r)/2;
if(judge(mid)>=k) l=mid;
else r=mid;
}
double ans=floor(r*100)/100;
//if(judge(ans)!=k) printf("0.00\n")<<endl; else printf("%.2f\n",ans);
printf("%.2f\n",ans);
return 0;
}
2、最大化最小值
poj 2456 Agressive cows 用cin cout T了
题意:N个牛棚C头牛,要求使任意两头牛之间的距离尽可能大
:可以安排牛的位置使得任意牛的间距不小于
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
int N,C;
const int M=1e5+10;
int loc[M];
bool judge(int x)
{
int cnt=1;
int lo=loc[1];
for(int i=2;i<=N;++i)
{
if(loc[i]>=lo+x) //一开始写得loc[i]==lo+x
{
++cnt;
lo=loc[i];
}
if(cnt>=C) return true;
}
return false;
}
int main()
{
scanf("%d%d",&N,&C);
for(int i=1;i<=N;++i)
scanf("%d",&loc[i]);
sort(loc+1,loc+1+N);
int l=0,r=loc[N]-loc[1];
while(l<=r)
{
int mid=(l+r)/2;
if(judge(mid))
l=mid+1;
else
r=mid-1;
}
printf("%d\n",r);
return 0;
}
题意:河两岸相距L units, 河中有N块石头,给出他们到此岸的距离,John想移走其中M块,使得任意两块石头之间的距离尽可能大
转化:从此岸跳到彼岸要跳石头数+1次。
:可以找到跳n+1-m次到达对岸且且最小距离不小于x,即每次距离都大于等于x
//poj 2456
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
int N,C;
const int M=1e5+10;
int loc[M];
bool judge(int x)
{
int cnt=1;
int lo=loc[1];
for(int i=2;i<=N;++i)
{
if(loc[i]>=lo+x) //一开始写得loc[i]==lo+x
{
++cnt;
lo=loc[i];
}
if(cnt>=C) return true;
}
return false;
}
int main()
{
scanf("%d%d",&N,&C);
for(int i=1;i<=N;++i)
scanf("%d",&loc[i]);
sort(loc+1,loc+1+N);
int l=0,r=loc[N]-loc[1];
while(r-l>1) //这种写法和double型的有很好的统一
{
int mid=(l+r)/2;
if(judge(mid))
l=mid;
else
r=mid;
}
printf("%d\n",l);
/* A 还是更喜欢这种写法
while(l<=r)
{
int mid=(l+r)/2;
if(judge(mid))
l=mid+1;
else
r=mid-1;
}
printf("%d\n",l-1); //输出r也是对哒
*/
return 0;
}
3、最小化最大值
题意:接下来N天农夫John每天要花money_i元,将这些天分为M季,每个季要花的钱尽可能少,每个季花的钱最少是多少
:可以选择M个分组使得每个分组的和不大于x
刷了几道题之后陷入了疑惑,输出到底应该是l还是l-1还是r?
//连暴力我都不知道怎么做 A了
#include<stdio.h>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N];
int n,m;
bool judge(int x)
{
int sum=0;
int cnt=0;
for(int i=1;i<=n;++i) //可以选择M个分组使得每个分组的和不大于x 最小化最大值
{
if(sum+a[i]<=x)
sum+=a[i];
else if(sum+a[i]>x)
{
sum=0;
++cnt;
sum+=a[i];
}
if(cnt+1>m) return false; //最后一个分组没被加进去;分的组数过多,价格应该调大一点
}
return true; //价格应该调小一点
}
int main()
{
scanf("%d%d",&n,&m);
int maxexp=0;
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
maxexp=max(maxexp,a[i]);
}
int l=maxexp,r=1e9+10; //一开始上界开小wa了
int mid;
while(l<=r)
{
mid=(l+r)/2;
if(judge(mid)) r=mid-1;
else l=mid+1;
}
printf("%d\n",l);
return 0;
}
4、最大化平均值
题意:给出N个test,从自己的分数中删掉K个,使得比分最大
审题不清楚导致输入错误,wa了,主要在于数据范围k>=0,可以为0
另外“.2f”可以自动四舍五入,如果不想四舍五入floor(l*100)/100 或者int(l*100)/100
:可以选择n-k个测试使得单位比分不小于x 比分是否可以不小于x
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<iostream>
using namespace std;
const int N=1010;
int n,k;
int a[N],b[N];
double c[N];
bool judge(double x) //可选的单位重量
{
for(int i=1;i<=n;++i)
c[i]=a[i]-x*b[i];
sort(c+1,c+1+n);
double sum=0;
for(int i=n;i>k;--i)
sum+=c[i];
return sum>=0;
}
int main()
{
// while(~scanf("%d%d",&n,&k)) // WA while(~scanf("%d%d",&n,&k)&&n&&k)
// {
// if(n==0&&k==0) break;
while(~scanf("%d%d",&n,&k)&&(n||k))
{
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
for(int i=1;i<=n;++i)
scanf("%d",&b[i]);
double l=0,r=1.0;
double mid;
while(r-l>1e-10)
{
mid=(l+r)/2;
if(judge(mid)) l=mid;
else r=mid;
}
// int ans=floor(l*100);
// printf("%d\n",ans); 会wa
printf("%.0f\n",l*100);
}
return 0;
}