下午
內容:二分分治模擬
廣告:推薦此題單
1. 二分
二分查詢
優點:在檢驗一個元素之後可以很快的捨棄掉一
半的元素,從而快速鎖定目標元素。
T1 數的劃分
問題簡述:輸入 \(n\) 個不超過 \(10^9\) 的單調不減的(就是後面的數字不小於前面的數字)非負整數 \(a_1,a_2,\dots,a_{n}\),然後進行 \(m\) 次詢問。對於每次詢問,給出一個整數 \(q\),要求輸出這個數字在序列中第一次出現的編號,如果沒有找到的話輸出 \(-1\) 。
思路:
直接二分查詢
std:
#include <iostream>
#define int long long
#define endl '\n'
using namespace std;
const int MAXN = 1e6 + 10;
int a[MAXN];
signed main(){
int n, m;
cin >> n >> m;
for(int i = 1;i <= n;i++){
cin >> a[i];
}
for(int i = 1;i <= m;i++){
int x;
cin >> x;
int l = 1, r = n;
while(l < r){
int mid = (l + r) / 2;
if(a[mid] >= x){
r = mid;
} else {
l = mid + 1;
}
}
if(a[l] != x){
cout << "-1 ";
} else {
cout << l << " ";
}
}
return 0;
}
記錄
二分答案
用處:有最大值最小或者最小值最大的描述時可以嘗試考慮二分答案 (反正你感覺很怪就對了)
T2 跳石頭
問題簡述:給你一排 \(N\) 塊石頭,你可以移走 \(M\) 塊石頭,使得最小的兩塊石頭之間的距離儘可能長。
思路:
直接二分查詢
std:
#include<bits/stdc++.h>
using namespace std;
int l , n , m;
int a[100010];
bool check(int d) {
int last = 0 , cnt = 0;
for(int i = 1 ; i <= n ; i ++) {
if(a[i] - last < d) cnt ++;
else last = a[i];
}
if(l - last < d) cnt ++;
return cnt <= m;
}
int main() {
scanf("%d%d%d", &l , &n , &m);
for(int i = 1 ; i <= n ; i ++) {
scanf("%d" , &a[i]);
}
int le = 1 , r = l + 1 , mid;
while(le + 1 < r) {
mid = (le + r) / 2;
if(check(mid)) le = mid;
else r = mid;
}
printf("%d", le);
return 0;
}
記錄
2. 分治
本質:分而治之
T3 跳石頭
問題簡述:組委會已經選擇好了兩塊岩石作為比賽起點和終點。在起點和終點之間,有 \(N\) 塊岩石(不含起點和終點的岩石)。在比賽過程中,選手們將從起點出發,每一步跳向相鄰的岩石,直至到達終點。為了提高比賽難度,組委會計劃移走一些岩石,使得選手們在比賽過程中的最短跳躍距離儘可能長。由於預算限制,組委會至多從起點和終點之間移走 \(M\) 塊岩石(不能移走起點和終點的岩石)。
思路:
std:
#include <iostream>
using namespace std;
const int MAXN = 1e6 + 10;
int a[MAXN];
int L, n , m;
bool check(int d){
int last = 0, cnt = 0;
for(int i = 1;i <= n;i++){
if(a[i] - last < d) cnt++;
else last = a[i];
}
if(L - last < d){
cnt++;
}
return cnt <= m;
}
int main(){
scanf("%d%d%d", &L , &n , &m);
for(int i = 1;i <= n;i++){
scanf("%d", &a[i]);
}
int l = 1 , r = L + 1 , mid;
while(l + 1 < r){
mid = (l + r) / 2;
if(check(mid)){
l = mid;
}
else{
r = mid;
}
}
printf("%d", l);
return 0;
}
記錄
T4 逆序對
問題簡述:對於給定的一段正整數序列,逆序對就是序列中 \(a_i>a_j\) 且 \(i<j\) 的有序對。知道這概念後,他們就比賽誰先算出給定的一段正整數序列中逆序對的數目。注意序列中可能有重複數字。
思路:
只要出現右側的值<左側的值的情況,就出現了逆序對。左側還剩多少個值,就會出現多少個逆序對。
再把所有分治時出現的所有逆序對的個數加起來就行了
std:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 500005;
int d[MAXN];
int a[MAXN],b[MAXN],c[MAXN];
int ans = 0;
void merge(int n,int m){
int aa = 1,bb = 1;
int cc = 0;
while(aa <= n && bb <= m){
if(a[aa] <= b[bb]){
cc ++;
c[cc] = a[aa];
ans += bb - 1;
aa ++;
}
else{
cc ++;
c[cc] = b[bb];
bb ++;
}
}
while(aa <= n){
cc ++;
c[cc] = a[aa];
ans += bb - 1;
aa ++;
}
while(bb <= m){
cc ++;
c[cc] = b[bb];
bb ++;
}
}
void guibing(int l,int r){
if(l >= r){
return;
}
int mid = (l+r)/2;
guibing(l,mid);
guibing(mid+1,r);
for(int i=l;i<=mid;++i){
a[i - l + 1] = d[i];
}
for(int i=mid+1;i<=r;++i){
b[i - mid] = d[i];
}
merge(mid - l + 1,r - mid);
for(int i=l;i<=r;++i)
d[i] = c[i - l + 1];
}
signed main(){
int n;
scanf("%lld",&n);
for(int i=1;i<=n;++i){
scanf("%lld",&d[i]);
}
guibing(1,n);
printf("%lld",ans);
}
記錄
3. 模擬
本質:按照題目的要求進行模擬操作
T5 鋪地毯
std:
#include<iostream>
using namespace std;
int a[100000], b[100000], g[100000], k[100000];
int main(){
int x, y, n ,s=-1;
cin >> n;
for(int i = 0;i < n;i++){
cin >> a[i] >> b[i] >> g[i] >> k[i];
}
cin >> x >> y;
for(int i = 0;i < n;i++){
if(x >= a[i]&&y >= b[i]&&x <= a[i] + g[i]&&y <= b[i] + k[i]){
s = i + 1;
}
}
cout << s;
}
記錄
T6 接水問題
std:
#include<cstdio>
using namespace std;
int w[10001], s[101], maxx;
int main(){
int n, m;
scanf("%d%d",&n,&m);
for (int i = 1;i <= n;i++){
scanf("%d", &w[i]);
}
for (int i = 1;i <= n;i++){
maxx = 1;
for (int j = 2;j <= m;j++)
if (s[maxx] > s[j]){
maxx = j;
}
s[maxx] += w[i];
}
maxx = 1;
for (int i = 1;i <= m;i++){
maxx = s[i] > maxx ? s[i] : maxx;
}
printf("%d",maxx);
}
記錄
T7 神奇的幻方
問題簡述: 簡述不了
std:
#include <bits/stdc++.h>
using namespace std;
int N, a[40][40], h, l;
int main(){
cin >> N;
a[0][N/2] = 1, h = 0, l = N / 2;
for(int i = 2;i <= N * N;i++){
if(h==0&&l!=N-1) a[N-1][l+1]=i,h=N-1,l++;
else if(l == N - 1&&h != 0) a[h - 1][0] = i,h--, l = 0;
else if(h == 0&&l == N - 1) a[h + 1][l] = i,h++;
else if(h != 0&&l != N - 1){
if(a[h - 1][l + 1] == 0&&h - 1 >= 0&&l + 1 <= N){
a[h - 1][l + 1] = i;
h--;
l++;
}
else a[h + 1][l] = i, h++;
}
}
for(int i = 0;i < N;i++){
for(int j = 0;j<N;j++){
cout << a[i][j] << ' ';
}
cout << endl;
}
}
記錄