分治總結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;
}

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