poj-1604 http://poj.org/problem?id=1064
题意:N条绳子,将它们剪成k段相同长度绳子,长度最长是多少?保留两位小数
思路:二分答案,然后判断是否满足条件,如果满足条件就增加长度,否则减少长度,直到找到答案。
代码:
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> #define INF 1<<29 using namespace std; int n,k; double l,r; double L[10005]; int judge(double x) { int num=0; for(int i=0;i<n;i++) num+=(int)(L[i]/x); return num>=k; } int main() { scanf("%d%d",&n,&k); l=0; r=INF; for(int i=0;i<n;i++) scanf("%lf",&L[i]); for(int i=0;i<100;i++) { double mid=(l+r)/2; if(judge(mid)) l=mid; else r=mid; } printf("%.2lf\n",floor(r*100)/100); return 0; }
像这样,如果在求解最大化最小化问题中,能够比较简单的判断条件是否满足,那么使用二分搜索是很好的解决方法。
对于小数问题,上述判断结束条件,固定了循环次数,100次循环可以精确到1e30,基本是没有问题的,还有一种方法是设置成(r-l)>eps,eps为精确度,但是这样子如果eps太小会陷入死循环。
poj-2456 http://poj.org/problem?id=2456
题意:约翰有N个牛舍,有M头牛,牛之间会互相攻击,所以约翰想使牛与牛之间的距离尽量大,求最大值。
思路:先对牛舍进行排序,然后将第一头牛放在第一个牛舍,然后二分枚举牛与牛之间的距离,然后判断是否满足条件。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,k; int l,r; int xx[100005]; int judge(int x) { int last=0; for(int i=1;i<k;i++) { int crt=last+1; while(crt<n&&xx[crt]-xx[last]<x) crt++; if(crt==n) return 0; last=crt; } return 1; } int main() { scanf("%d%d",&n,&k); for(int i=0;i<n;i++) scanf("%d",&xx[i]); sort(xx,xx+n); l=0; r=xx[n-1]-xx[0]; //最大距离是排序后第一个牛舍和最后一个牛舍之间的距离 while(r-l>1) { int mid=(l+r)/2; if(judge(mid)) l=mid; else r=mid; } printf("%d\n",l); return 0; }
poj-3258 http://poj.org/problem?id=3258
题意:一条和宽L,河中有N个垫脚石,然后给出N个垫脚石的位置,然后两个河岸的位置分别是0和L,然后约翰可以移走M个垫脚石,求垫脚石之间最小距离的最大值。
思路:首先,N个垫脚石+两个河岸相当于N+2个垫脚石,然后对垫脚石位置从小到大排序。然后确定二分的范围,之后判断是否满足条件。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int L,N,M; int vis[50005]; int solve(int x) { int cnt=0; int t=0; int e=1; while(e<N) { if(vis[e]-vis[t]>=x) //如果当前垫脚石与前一个垫脚石的距离大于等于x;更新垫脚石 { t=e; e++; } else //如果相邻两个垫脚石的距离小于x,则移走当前垫脚石,移动的数量加一,更新垫脚石 { e++; cnt++; } } if(cnt>M) //如果移动的次数大于M说明最小值的最大值过大,要减小最小值的最大值 return 0; else return 1; } int main() { scanf("%d%d%d",&L,&N,&M); vis[0]=0; for(int i=1;i<=N;i++) scanf("%d",&vis[i]); vis[N+1]=L; N=N+2; sort(vis,vis+N); int l=0; int r=vis[N-1]; while(l<=r) { int mid=(l+r)/2; if(solve(mid)) l=mid+1; else r=mid-1; } printf("%d\n",r); return 0; }
poj-3273 http://poj.org/problem?id=3273
题意:农夫约翰每天都有一定的零花钱,然后他规定了一个“月”,然后将N天变成M月,然后求他在这些月中可以花的钱的最大值。
思路:不断的二分枚举所能花的最大值,然后判断能否将N天分成M段,并且不超过最大值。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int N,M; int vis[100005]; int solve(int x) { int sum=0; int cnt=1; for(int i=0;i<N;i++) { if(sum+vis[i]<=x) sum+=vis[i]; else sum=vis[i],cnt++; } if(cnt>M) return 1; else return 0; } int main() { scanf("%d%d",&N,&M); int l=-1,r=0; for(int i=0;i<N;i++) { scanf("%d",&vis[i]); l=max(l,vis[i]); r+=vis[i]; } while(l<=r) { int mid=(l+r)/2; if(solve(mid)) l=mid+1; else r=mid-1; } printf("%d\n",l); return 0; }
poj-3104 晒衣服 http://poj.org/problem?id=310
题意:简是个聪明的女孩,他要晒N件衣服,每件衣服都有自己的含水量,每过一分钟,每件衣服的含水量会减少1,如果将衣服放在烘干机中,每分钟含水量减少看,问简晒玩这些衣服需要多久。
思路:这题和之前一场Atcoder的题目一样,当时没有做出来,然后也没有找到解那道题的博客,所以当时就没有补。刚好写这道题的时候,想起来那道题,之后把他补了,附上连接:http://abc063.contest.atcoder.jp/tasks/arc075_b然后写这道题的时候有了一点思路,但是没考虑全面,假设用了x分钟,如果含水量小于等于x的衣服则自然晒干,大于x的衣服就需要用烘干机了,但是我们首先将他的含水量减去x,如果需要用烘干机烘干的话,说明他每分钟可以减少k,因为我们之前先减去了x,所以他用烘干机相当于每分钟减少了k-1的含水量,这样对于时间的判断就很容易写了。用二分就可以了。注意用long long!
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; #define ll long long int n,k; ll vis[100005]; ll sum; int solve(ll T) { ll t=T; for(int i=0;i<n;i++) { ll x=vis[i]-T; if(x>0) t-=ceil(x*1.0/(k-1)); //如果除不尽的话肯定还需要一分钟,向上取整就可以了 } if(t>=0) return 1; else return 0; } int main() { while(scanf("%lld",&n)!=EOF) { sum=0; for(int i=0;i<n;i++) { scanf("%d",&vis[i]); sum+=vis[i]; } sort(vis,vis+n); scanf("%d",&k); if(k==1) printf("%lld\n",vis[n-1]); else { ll l=1; ll r=vis[n-1]; while(l<=r) { ll mid=(l+r)/2; if(solve(mid)) r=mid-1; else l=mid+1; } printf("%lld\n",l); } } return 0; }
poj-2976 http://poj.org/problem?id=2976
题意:期末,你有n场考试,每场考试你做对了b道题目中的a道,你可以扔掉k门科目,然后用剩下的科目成绩去计算总成绩,总成绩等于100*(所选科目答对的题目总和/所选科目的总题数),求最大成绩。
思路:设单位题目的正确率不小于x,则所选科目答对的题目总和/所选科目的总题数>=x,变形的∑(ai-x*bi)>=0;因此可以对ai-x*bi排序进行选取,这样就变成了使得前(n-k)项的和大于等于0
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define MAX 1005 int v[MAX]; int w[MAX]; double y[MAX]; int n,k; int solve(double x) { for(int i=0;i<n;i++) y[i]=v[i]-x*w[i]; sort(y,y+n); double sum=0; for(int i=0;i<n-k;i++) sum+=y[n-i-1]; return sum>=0; } int main() { while(scanf("%d%d",&n,&k)!=EOF&&n) { for(int i=0;i<n;i++) scanf("%d",&v[i]); for(int i=0;i<n;i++) scanf("%d",&w[i]); double l=0; double r=1e10; for(int i=0;i<100;i++) { double mid=(l+r)/2; if(solve(mid)) l=mid; else r=mid; } int ans=100*(l+0.005); printf("%d\n",ans); } return 0; }
poj-3111 http://poj.org/problem?id=3111
题意:n件商品,编号1-n,每件有价格和重量,从中选出k件,为了使单位重量的价格最大,应如何选取,输出商品编号。
思路:和上题一样。多了保存原来的位置
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define MAX 100005 #define eps 1e-8 struct Node{ int v; int w; int xx; double y; bool operator < (const Node &a)const{ return y>a.y; } }node[MAX]; int n,k; int solve(double x) { for(int i=0;i<n;i++) node[i].y=node[i].v-x*node[i].w; sort(node,node+n); double sum=0; for(int i=0;i<k;i++) sum+=node[i].y; return sum>=0; } int main() { scanf("%d%d",&n,&k); int sum=0; for(int i=0;i<n;i++) { scanf("%d%d",&node[i].v,&node[i].w); node[i].xx=i+1; sum+=node[i].v; } double l=0; double r=sum; while(r-l>eps) { double mid=(l+r)/2; if(solve(mid)) l=mid; else r=mid; } for(int i=0;i<k-1;i++) printf("%d ",node[i].xx); printf("%d\n",node[k-1].xx); return 0; }
最近复习了一下二分,之前不是很熟练,然后复习之后也对二分有了更深的认识,然后把今天写的题总结一下。题目来源于《挑战程序设计竞赛》。
英语水平很烂,题意应该还算清晰吧。写的不好的地方,也希望大家能指出来,应该也没有人会看到。
poj-1604 http://poj.org/problem?id=1064
题意:N条绳子,将它们剪成k段相同长度绳子,长度最长是多少?保留两位小数
思路:二分答案,然后判断是否满足条件,如果满足条件就增加长度,否则减少长度,直到找到答案。
代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
#include<iostream>#include<cstring>#include<cstdio>#include<cmath>#include<algorithm>#define INF 1<<29using namespace std;int n,k;double l,r;double L[10005];int judge(double x){ int num=0; for(int i=0;i<n;i++) num+=(int)(L[i]/x); return num>=k;}int main(){ scanf("%d%d",&n,&k); l=0; r=INF; for(int i=0;i<n;i++) scanf("%lf",&L[i]); for(int i=0;i<100;i++) { double mid=(l+r)/2; if(judge(mid)) l=mid; else r=mid; } printf("%.2lf\n",floor(r*100)/100); return 0;} |
像这样,如果在求解最大化最小化问题中,能够比较简单的判断条件是否满足,那么使用二分搜索是很好的解决方法。
对于小数问题,上述判断结束条件,固定了循环次数,100次循环可以精确到1e30,基本是没有问题的,还有一种方法是设置成(r-l)>eps,eps为精确度,但是这样子如果eps太小会陷入死循环。
poj-2456 http://poj.org/problem?id=2456
题意:约翰有N个牛舍,有M头牛,牛之间会互相攻击,所以约翰想使牛与牛之间的距离尽量大,求最大值。
思路:先对牛舍进行排序,然后将第一头牛放在第一个牛舍,然后二分枚举牛与牛之间的距离,然后判断是否满足条件。
代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int n,k;int l,r;int xx[100005];int judge(int x){ int last=0; for(int i=1;i<k;i++) { int crt=last+1; while(crt<n&&xx[crt]-xx[last]<x) crt++; if(crt==n) return 0; last=crt; } return 1;}int main(){ scanf("%d%d",&n,&k); for(int i=0;i<n;i++) scanf("%d",&xx[i]); sort(xx,xx+n); l=0; r=xx[n-1]-xx[0]; //最大距离是排序后第一个牛舍和最后一个牛舍之间的距离 while(r-l>1) { int mid=(l+r)/2; if(judge(mid)) l=mid; else r=mid; } printf("%d\n",l); return 0;} |
poj-3258 http://poj.org/problem?id=3258
题意:一条和宽L,河中有N个垫脚石,然后给出N个垫脚石的位置,然后两个河岸的位置分别是0和L,然后约翰可以移走M个垫脚石,求垫脚石之间最小距离的最大值。
思路:首先,N个垫脚石+两个河岸相当于N+2个垫脚石,然后对垫脚石位置从小到大排序。然后确定二分的范围,之后判断是否满足条件。
代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int L,N,M;int vis[50005];int solve(int x){ int cnt=0; int t=0; int e=1; while(e<N) { if(vis[e]-vis[t]>=x) //如果当前垫脚石与前一个垫脚石的距离大于等于x;更新垫脚石 { t=e; e++; } else //如果相邻两个垫脚石的距离小于x,则移走当前垫脚石,移动的数量加一,更新垫脚石 { e++; cnt++; } } if(cnt>M) //如果移动的次数大于M说明最小值的最大值过大,要减小最小值的最大值 return 0; else return 1;}int main(){ scanf("%d%d%d",&L,&N,&M); vis[0]=0; for(int i=1;i<=N;i++) scanf("%d",&vis[i]); vis[N+1]=L; N=N+2; sort(vis,vis+N); int l=0; int r=vis[N-1]; while(l<=r) { int mid=(l+r)/2; if(solve(mid)) l=mid+1; else r=mid-1; } printf("%d\n",r); return 0;} |
poj-3273 http://poj.org/problem?id=3273
题意:农夫约翰每天都有一定的零花钱,然后他规定了一个“月”,然后将N天变成M月,然后求他在这些月中可以花的钱的最大值。
思路:不断的二分枚举所能花的最大值,然后判断能否将N天分成M段,并且不超过最大值。
代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int N,M;int vis[100005];int solve(int x){ int sum=0; int cnt=1; for(int i=0;i<N;i++) { if(sum+vis[i]<=x) sum+=vis[i]; else sum=vis[i],cnt++; } if(cnt>M) return 1; else return 0;}int main(){ scanf("%d%d",&N,&M); int l=-1,r=0; for(int i=0;i<N;i++) { scanf("%d",&vis[i]); l=max(l,vis[i]); r+=vis[i]; } while(l<=r) { int mid=(l+r)/2; if(solve(mid)) l=mid+1; else r=mid-1; } printf("%d\n",l); return 0;} |
poj-3104 晒衣服 http://poj.org/problem?id=310
题意:简是个聪明的女孩,他要晒N件衣服,每件衣服都有自己的含水量,每过一分钟,每件衣服的含水量会减少1,如果将衣服放在烘干机中,每分钟含水量减少看,问简晒玩这些衣服需要多久。
思路:这题和之前一场Atcoder的题目一样,当时没有做出来,然后也没有找到解那道题的博客,所以当时就没有补。刚好写这道题的时候,想起来那道题,之后把他补了,附上连接:http://abc063.contest.atcoder.jp/tasks/arc075_b然后写这道题的时候有了一点思路,但是没考虑全面,假设用了x分钟,如果含水量小于等于x的衣服则自然晒干,大于x的衣服就需要用烘干机了,但是我们首先将他的含水量减去x,如果需要用烘干机烘干的话,说明他每分钟可以减少k,因为我们之前先减去了x,所以他用烘干机相当于每分钟减少了k-1的含水量,这样对于时间的判断就很容易写了。用二分就可以了。注意用long long!
代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>using namespace std;#define ll long longint n,k;ll vis[100005];ll sum;int solve(ll T){ ll t=T; for(int i=0;i<n;i++) { ll x=vis[i]-T; if(x>0) t-=ceil(x*1.0/(k-1)); //如果除不尽的话肯定还需要一分钟,向上取整就可以了 } if(t>=0) return 1; else return 0;}int main(){ while(scanf("%lld",&n)!=EOF) { sum=0; for(int i=0;i<n;i++) { scanf("%d",&vis[i]); sum+=vis[i]; } sort(vis,vis+n); scanf("%d",&k); if(k==1) printf("%lld\n",vis[n-1]); else { ll l=1; ll r=vis[n-1]; while(l<=r) { ll mid=(l+r)/2; if(solve(mid)) r=mid-1; else l=mid+1; } printf("%lld\n",l); } } return 0;} |
poj-2976 http://poj.org/problem?id=2976
题意:期末,你有n场考试,每场考试你做对了b道题目中的a道,你可以扔掉k门科目,然后用剩下的科目成绩去计算总成绩,总成绩等于100*(所选科目答对的题目总和/所选科目的总题数),求最大成绩。
思路:设单位题目的正确率不小于x,则所选科目答对的题目总和/所选科目的总题数>=x,变形的∑(ai-x*bi)>=0;因此可以对ai-x*bi排序进行选取,这样就变成了使得前(n-k)项的和大于等于0
代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define MAX 1005int v[MAX];int w[MAX];double y[MAX];int n,k;int solve(double x){ for(int i=0;i<n;i++) y[i]=v[i]-x*w[i]; sort(y,y+n); double sum=0; for(int i=0;i<n-k;i++) sum+=y[n-i-1]; return sum>=0;}int main(){ while(scanf("%d%d",&n,&k)!=EOF&&n) { for(int i=0;i<n;i++) scanf("%d",&v[i]); for(int i=0;i<n;i++) scanf("%d",&w[i]); double l=0; double r=1e10; for(int i=0;i<100;i++) { double mid=(l+r)/2; if(solve(mid)) l=mid; else r=mid; } int ans=100*(l+0.005); printf("%d\n",ans); } return 0;} |
poj-3111 http://poj.org/problem?id=3111
题意:n件商品,编号1-n,每件有价格和重量,从中选出k件,为了使单位重量的价格最大,应如何选取,输出商品编号。
思路:和上题一样。多了保存原来的位置
代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define MAX 100005#define eps 1e-8struct Node{ int v; int w; int xx; double y; bool operator < (const Node &a)const{ return y>a.y; }}node[MAX];int n,k;int solve(double x){ for(int i=0;i<n;i++) node[i].y=node[i].v-x*node[i].w; sort(node,node+n); double sum=0; for(int i=0;i<k;i++) sum+=node[i].y; return sum>=0;}int main(){ scanf("%d%d",&n,&k); int sum=0; for(int i=0;i<n;i++) { scanf("%d%d",&node[i].v,&node[i].w); node[i].xx=i+1; sum+=node[i].v; } double l=0; double r=sum; while(r-l>eps) { double mid=(l+r)/2; if(solve(mid)) l=mid; else r=mid; } for(int i=0;i<k-1;i++) printf("%d ",node[i].xx); printf("%d\n",node[k-1].xx); return 0;} |

浙公网安备 33010602011771号