分治总结1

问题 K: 【分治】化装晚会

题目
万圣节又到了!FJ打算带他的奶牛去参加化装晚会,但是,FJ只做了一套能容下两头总长不超过S (1≤S≤1000000)的奶牛恐怖服装。FJ养了N(2≤N≤20000)头按1–N顺序编号的奶牛,编号为i的奶牛的长度为L_i(1≤L_i≤1000000)。如果两头奶牛的总长度不超过S,那么她们就能穿下这套服装。
FJ想知道,如果他想选择两头不同的奶牛来穿这套衣服,一共有多少种满足条件的方案。
输入
第1行是2个整数:N和S;
第2~N+l行每行一个整数:L_i。
输出
1个整数,表示FJ可选择的所有方案数。注意奶牛顺序不同的两种方案是被视为相同的。
思路
首先想到可以排序后对每头牛i二分搜索和它满足条件的第二头牛j,,则比j长度小的牛也都满足条件,枚举第一头牛+二分搜索,复杂度O(nlogn)。
如果从大向小枚举i,那么其实没必要每次重新二分找j,因为满足前一头牛的j一定满足当前的牛。所以j可以按从小到大的顺序移动。复杂度降为O(n)。
细节见代码:

#include <iostream>
#include <algorithm>
#define rep(i,j,n) for(register int i=j;i>=n;i--)
using namespace std;
int a[20005];
int main()
{
    int n,s;
    cin>>n>>s;
    for(int i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+n+1);
    int cnt=0;
    int j=1;
    rep(i,n,1)
    {
        while(j<=n&&a[i]+a[j]<=s) j++;
        if(i>=j) cnt+=j-1;
        else cnt+=j-2;
    }
    cout << cnt/2 << endl;
    return 0;
}


问题 C: 【分治】循环赛问题

题目:
设有n个选手的网球循环比赛,其中n=2k(0≤k<7)。现要设计一个满足以下条件的比赛日程表:
(1)每名选手要与其他n-1名选手都进行一次比赛;
(2)每名选手每天只赛一次;
(3)整个比赛共进行n-1天,要求每天没有选手轮空。
输入
选手人数n(n≤100),n只能为2的整数次幂。
输出
N阶方阵A[l…n,0…n-l],当j>0时,A[i,j]表示第i名运动员在第j天所遇到的比赛对手(A[i,0]=i),每个数据占5位宽度。
思路
如下图所示是k=3时的一个可行解(第1列是选手编号),它是4块拼起来的。 左上角是k=2时的一组解,左下角是左上角每个数加4得到,而右上角、 右下角分别由左下角、 左上角复制得到。
在这里插入图片描述
代码

#include <iostream>
#include <cstdio>
using namespace std;
int ans[105][105];
void slove(int k,int n)
{
	for(int i=1;i<=n;i++) ans[1][i]=i;
    int start=1;
	for(int p=1;p<=k;p++)
	{
		n/=2;
		for(int t=1; t<=n; t++)
		{
			for(int i=start+1; i<=2*start; i++)//控制行
			{
				for(int j=start+1; j<=2*start; j++)//控制列
				{
					ans[i][j+(t-1)*start*2] = ans[i-start][j+(t-1)*start*2-start];//右下角等于左上角的值
					ans[i][j+(t-1)*start*2-start] = ans[i-start][j+(t-1)*start*2];//左下角等于右上角的值
				}

			}
		}
		start *= 2;
	}
}
int main()
{
    int k=0,n;cin>>n;
    int m=n;
    while(m) m/=2,k++;
    slove(k,n);
    for(int i=1;i<=n;i++) {for(int j=1;j<=n;j++) printf("%5d",ans[i][j]);cout<<endl;}
    return 0;
}


问题 R: 【分治】小车问题

题目
甲、乙两人同时从A地出发要尽快同时赶到B地。出发时A地有一辆小车,可是这辆小车除了驾驶员外只能带一人。已知甲、乙两人的步行速度一样,且小于车的速度。问:怎样利用小车才能使两人尽快同时到达。
输入
仅一行,三个数据分别表示AB两地的距离s,人的步行速度a,车的速度b。
输出
两人同时到达B地需要的最短时间(答案保留两位小数)。
思路
1)二分:根据题意可得知,小车载其中一人一定距离后,必要返回接另一个人,使得两人耗时相同且最小。
于是,我们就不妨二分小车第一次载甲的行驶距离S’(甲乙先后顺序不影响),计算出T甲,T乙。
如果T甲>T乙,则说明小车载甲的行驶距离太短,反之则太长。
代码

#include<cstdio>
#include<iostream>
using namespace std;
double S,v1,v2;
int main()
{
	scanf("%lf%lf%lf",&S,&v1,&v2);
	double l=0,r=S,mid,t1,t2;
	while(r-l>=0.0001)
	{
		mid=(l+r)/2;
		t1=mid/v2+(S-mid)/v1;
		t2=(2*mid*(v2-v1)+S*(v1+v2))/(v2*(v1+v2));
		if(t1>t2)l=mid;
		else r=mid;
	}
	
	printf("%0.4lf\n",l/v2+(S-l)/v1);
}

2)推公式

#include<iostream>
#include<cstdio>
using namespace std;

int main()
{
    double s,v1,v2;
    cin>>s>>v1>>v2;
    double ans=s*(v2*v2-v1*v1)/(2*v1*v2-3*v1*v1+v2*v2);
    double ANS=ans/v2+(s-ans)/v1;
    printf("%0.2lf\n",ANS);
}


问题 M: 【分治】木材加工

题目:

木材厂有一些原木,现在想把这些木头切割成一些长度相同的小段木头(木头可能有剩余),需要得到的小段的数目是给定的。当然,我们希望得到的小段越长越好,你的任务是计算能够得到的小段木头的最大长度。木头长度的单位是cm。原本的长度都是正整数,我们要求得到的小段木头的长度也是正整数。
输入
第一行是两个正整数N和K(1≤N≤100000,1≤K≤200000),N是原木的数目,K是需要得到的小段的数目。接下来的N行,每行有一个1到10000之间的正整数,表示一根原木的长度L。
输出
输出能够切割得到的小段的最大长度。如果连lcm长的小段都切不出来,输出“0”.
思路
令l=1,r=10000然后二分区间,若中点mid符合条件,则向右区间寻找,否则向左区间寻找,直到 l==r,r是最大的长度,输出r

#include <iostream>
#include <algorithm>
using namespace std;
int a[100005];
int n,k;
bool judge(int aver)
{
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        cnt+=a[i]/aver;
    }
    return cnt>=k;
}
int main()
{

    cin>>n>>k;
    int sum=0;
    for(int i=1;i<=n;i++) cin>>a[i];
    int l=1,r=10000,mid;
    while(l<=r)
    {
        mid=(l+r)/2;
        if(judge(mid))  l=mid+1;
        else            r=mid-1;
    }
    cout<<r<<endl;
    return 0;
}


问题 O: 【分治】奇怪的函数

题目:
自从得到上次的教训后,John的上课态度认真多了,也变得更爱动脑筋了。今天他又学习了一个新的知识:关于 xk 的位数。
如果x大于0小于l,那么位数=1+小数部分×k,
如果x≥l,那么位数=trunc(ln(x)/ln(10)×k)+1+小数部分×k。
根据这些函数知识,他学会了求xk的位数了。但他又想到了另外一个问题,如果已知位数N,能不能求出使得 xk 达到或超过N位数字的最小正整数x是多少?
输入
输入一个正整数n(n≤2000000000)。
输出
输出使得 xk 达到n位数字的最小正整数x。
思路:二分区间[1,2000000000],是否符合条件 mid*log10(mid)+1>=n,是r=mid-1,否l=mid+1最后输出L即可;(与上一题M区分开来,M输出的是R)
代码

#include <iostream>
#include <cmath>
using namespace std;
int main()
{
    int n;cin>>n;
    int l=1;int r=2e9;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(mid*log10(mid)+1>=n) r=mid-1;///是log10,log是以e为底的
        else l=mid+1;
    }
    cout << l << endl;
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章