1. 程式人生 > 其它 >省賽訓練 2 3.15

省賽訓練 2 3.15

G. The Math of Sailing

題意:

一共有4塊布 可以對任意一塊布進行裁剪 裁剪後排列 成a1 a2 a3 a4 使得滿足 a1a4+a2+a3=a1+a4+a2a3. 且要求等式左邊值最大 

輸出兩行 第一行p[i] 對應是第幾塊裁剪過來的 第二行 x[i] 對應是當前位置的布的大小

思路: 

對上面那個公式進行處理 可以得到 (a1 - 1)(a4 - 1) = (a3 - 1)(a2 - 1) 即 裁剪排列後的布的大小滿足前面的公式 將ai-1看成pi 可以確定p1的到小一定最小 p4的大小一定最大 那麼就只要把a進行升序排列就能確定 那兩個組合了 然後判斷 兩邊式子 的大小 如果 左邊 > 右邊 就減小 a1使得等式成立 否則 減小a2使得等式成立 

為什麼一定是減小小的那個才能第一個等式使的左右兩邊的值最大 :

設 A = a1 + a2  + a3 + a4 若(a1 - 1)(a4 - 1) < (a3 - 1)(a2 - 1) a1 和 a4 固定不變 a1 + a4 + a2 * a3 = A + (a2 - 1) (a3 - 1) + 1 因為(a2 - 1)(a3 - 1)已經固定 只要使得 a2 + a3最大即可 根據數學 知識就知道要減小最小的那個數 兩者的和才會最大

#include <bits/stdc++.h> 
#include <cstdlib>
#include <cmath>
#define
ll long long #define FF ios::sync_with_stdio(false),cin.tie(0); using namespace std; const int N = 1e5 + 10; ll n, m, cnt; double b[10]; struct node{ int num; double x; }a[5]; bool comp(node a, node b){ return a.x < b.x; } void solve(){ for(int i = 1; i <= 4; i++){ cin
>> a[i].x; a[i].num = i;//記錄布的編號和大小 } sort(a + 1, a + 5, comp);//從按布的大小 升序 if(a[1].x == 1) {//如果a[1].x == 1 說明 a[1].x - 1 == 0 那麼乘積為 0 a[2].x - 1也一定要是0 ] a[2].x = 1; for(int i = 1; i <= 4; i++){ cout << a[i].num << " \n"[i == 4]; } for(int i = 1; i <= 4; i++){//精度要開的大一點 否則可能過不了 cout << fixed << setprecision(12) << a[i].x << " \n"[i == 4]; } } else{ if((a[1].x - 1) * (a[4].x - 1) < (a[2].x - 1) * (a[3].x - 1)){ a[2].x = ((a[1].x - 1) * (a[4].x - 1)) / (a[3].x - 1) + 1;//較小值變小 } else { a[1].x = ((a[2].x - 1) * (a[3].x - 1)) / (a[4].x - 1) + 1; } for(int i = 1; i <= 4; i++){ cout << a[i].num << " \n"[i == 4]; } for(int i = 1; i <= 4; i++){ cout << fixed << setprecision(12) << a[i].x << " \n"[i == 4]; } } } int main() { FF; cout.setf(ios::fixed); int t = 1; while(t --){ solve(); } return 0; }

 

 

D. Exam registration

題意:

給出 n 天想要考試的人數 和 最多可以考試的人數 當想要參加考試的人數多於規定的最大人數時 可以將一個同學移到另一天 要求所有同學中移動次數最多的最小值 如果無法讓所有同學都參加考試輸出-1

思路:

用二分做 二分答案 即 所有同學中移動最多的同學的最小值 如果滿足右邊界往左移 否則往左邊界往右移 (之前先判斷 預約人數是否超過限制人數 如果超過了就直接輸出-1)

每次二分查詢的時候 可以 初始化aa bb 兩個陣列 然後 指定一個左邊界代表人數沒有滿的最早的日子 從左到右遍歷 優先將左邊日子的人數填滿 還要控制一下天數的差在 正在查詢的 ans的範圍內 如果左邊界超出n 說明 ans 太小了 左邊界要增大 否則繼續查詢更小的 ans

#include <bits/stdc++.h> 
#include <queue>
#define ll long long
#define pi acos(-1)
#define FF ios::sync_with_stdio(false), cin.tie(0)
using namespace std;
const int mod = 1e9 + 7;
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f;
ll n, m, a[N], b[N], aa[N], bb[N];
ll ans, sum1, sum2;
 
void solve(){
    cin >> n;
    ll ans = inf;
    for(int i = 1; i <= n; i++){
        cin >> a[i];
        sum1 += a[i];//計算總預約人數 
    }
    for(int i = 1; i <= n; i++){
        cin >> b[i];
        sum2 += b[i];//計算總限制人數 
    }
    if(sum1 > sum2) {如果預約人數比限制人數大 輸出-1
        cout << "-1\n";
        return;
    }
    ll l = 0, r = n - 1;//二分答案 
    while(l <= r){
        ll mid = (l + r) / 2;
        int flag = 1;
//初始化 aa bb 陣列 便於後續處理
for(int i = 1; i <= n; i++){ aa[i] = a[i]; bb[i] = b[i]; } int pre = 1;//設定一個左邊界
//判斷該ans 是否滿足要求
for(int i = 1; i <= n; i++){ if(pre + mid < i) pre++;//讓左邊界再當前i的答案範圍內 if(bb[pre] >= aa[i]){//限制人數 > 預約人數 bb[pre] -= aa[i]; if(bb[pre] == 0) pre++;//當天人已滿 左邊界右移 指向下一天 } else{ while(aa[i]){//當預約人數更多時 if(pre > i + mid || pre > n){//左邊界超出答案範圍或超出陣列大小 都是不滿足 flag = 0;//標記一下 跳出 break; } if(aa[i] >= bb[pre]){// aa[i] -= bb[pre]; pre++; } else{ bb[pre] -= aa[i]; aa[i] = 0; } } if(!flag) break; } } if(flag) r = mid - 1;//如果答案滿足要求繼續查詢更小的答案 else l = mid + 1;//如果答案不滿足 增大答案值 } cout << l << "\n"; } int main() { FF; int t = 1; while(t --){ solve(); } return 0; }

 

 

E. Fair Robbery

題意:

給出 n 個人的財產 對於k 每次可以偷編號為k - n的人的t%的錢 要求投完後這n個人的錢 的最大值和最小值之差 最小 (差最小的前提下如果能偷更多的錢就偷更多)對於每個k(1-n) 求最優的t的值 精度至少1e9 

思路:

對於 k = 1 把所有人的錢偷了他們就都一樣了 差值為0 t = 1

對於k >= 2

首先 預處理 開個結構體陣列 記入每個i 前i 數的最大值和最小值 以及從i開始字尾的最大值和最小值 

對於k 如果字尾的最大值 大於字首的最大值 有兩種情況:

  • 字尾的最小值小於字首的最小值                  把字尾的最大值變成和字首的最大值一樣 就可以達到最優 
  • 字尾的最小值比字首的大                                      考慮把字尾的最大值變成字首的最大值 和 把字尾的最小值改成字首的最小值那個更優 

如果 字尾的最大值小於字首的最大值 也有兩種情況

  • 字尾的最小值小於字首的最小值                            字尾不能在變 不然最小值也會變小 差值會變大
  • 字尾的最小值比字首的大 字尾怎麼改都行因為     最小最大已經固定 那麼要求偷得最多 就把字尾的最小值變成字首的最小值即可
#include <bits/stdc++.h> 
#include <queue>
#define ll long long
#define pi acos(-1)
#define FF ios::sync_with_stdio(false)// cin.tie(0)
using namespace std;
const int mod = 1e9 + 7;
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f;
ll n, m, a[N], b[N], maxx[N], minn[N];
ll ans, sum1, sum2;
 
struct node {
    ll pmx, pmn, bmx, bmn;
}mm[N];
 
void solve(){
    cin >> n;
//預處理記入字首 最大最小值
for(int i = 1; i <= n; i++){ cin >> a[i]; if(i == 1){ mm[i].pmx = a[i]; mm[i].pmn = a[i]; } else{ mm[i].pmx = max(a[i], mm[i - 1].pmx); mm[i].pmn = min(a[i], mm[i - 1].pmn); } }
//預處理字尾的最大最小值
for(int i = n; i >= 1; i--){ if(i == n){ mm[i].bmx = a[i]; mm[i].bmn = a[i]; } else{ mm[i].bmx = max(a[i], mm[i + 1].bmx); mm[i].bmn = min(a[i], mm[i + 1].bmn); } } for(int i = 1; i <= n; i++){
//k == 1
if(i == 1) cout << fixed << setprecision(12) << 1.0 << " \n"[i == n]; else{ if(mm[i - 1].pmx >= mm[i].bmx && mm[i - 1].pmn <= mm[i].bmn)//情況4 cout << fixed << setprecision(12) << 1 - (double)mm[i - 1].pmn / mm[i].bmn << " \n"[i == n]; else if(mm[i - 1].pmx <= mm[i].bmx && mm[i - 1].pmn >= mm[i].bmn){//情況 1 cout << fixed << setprecision(12) << 1 - mm[i - 1].pmx / (double)mm[i].bmx << " \n"[i == n]; //else cout << fixed << setprecision(12) << 0.0 << " \n"[i == n]; } else if(mm[i].bmx >= mm[i - 1].pmx) {//情況2 double mi = min((double)mm[i - 1].pmn, mm[i].bmn * (mm[i - 1].pmx / (double)mm[i].bmx)); double ma = max((double)mm[i - 1].pmx, mm[i].bmx * (mm[i - 1].pmn / (double)mm[i].bmn)); if(mm[i - 1].pmx - mi < ma - mm[i - 1].pmn) cout << fixed << setprecision(12) << 1 - mm[i - 1].pmx / (double)mm[i].bmx << " \n"[i == n]; else cout << fixed << setprecision(12) << 1 - mm[i - 1].pmn / (double)mm[i].bmn << " \n"[i == n]; }//情況3 else cout << fixed << setprecision(12) << 0.0 << " \n"[i == n]; } } } int main() { FF; cout.setf(ios::fixed); int t = 1; while(t --){ solve(); } return 0; }