日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

牛客算法課 (算法入門班) 貪心與模擬(4)

發布時間:2023/12/14 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 牛客算法課 (算法入門班) 貪心與模擬(4) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

相差最大(貪心策略列子1)

[NOIP1998]拼數

區間覆蓋

NC16561 國王的游戲

NC25043 protecting the flower

NC10712 CF484A Bits

NC1879 毒瘤xor

NC20860 兔子的區間密碼

起床困難綜合癥


對於任何二進制題目, 我們都要考慮是否可以每一位單獨考慮!

相差最大(貪心策略列子1)

給定長度為n的整數數列{Ai}, 找出兩個整數 ai 和 aj (i < j), 使得ai - aj盡量的大.

暴力一定是固定一個點ai, 枚舉aj, 然後打擂臺.

你會發現如果我們把後綴最小的aj 提前算出來, 我們就可以省略一個nested loop去找最小aj了.

又或者你固定aj, 去找前綴最大ai.

思維講解 沒有代碼

[NOIP1998]拼數

一開始直接有一個思路就是按照字典序排序, 然後從大到小合拼, 但是, 由於每個數字的長度不一樣, 所以有機會出錯, 我們可以考慮, 比較的時候, 讓a + b 和 b + a 去比字典序, 那麼長度的憂慮就可以消除了

#include<bits/stdc++.h> using namespace std; bool cmp(string a, string b){return a + b > b + a; } int main(){int n;cin >> n;vector<string> v;for(int i = 0; i < n; i++){string ccc;cin >> ccc;v.push_back(ccc);}sort(v.begin(), v.end(), cmp);string res = "";for(auto a : v){res += a;}cout << res<< endl;return 0; }

區間覆蓋

給出n個區間, 讓你找出最多的區間,并且他們不想交.

可以利用貪心, 把他按照右端點進行排序, 因為只要一個區間的右端點靠前, 那么右端點比他大的區間能與靠后的區間成為一個子集, 那么靠前的那個區間也一定行. 利用這個思想就可以輕松把題目做出來.

NC16561 國王的游戲

套路題, 如果任意兩個相鄰的人交換不會影響后面的人和前面的人的結果, 我們可以進行推論.

設前面的人的乘積是pi. ab 為相鄰的兩個人.?

對于 a b 這種排法. a的答案為 pi / a.right?, b 的答案為 pi * a.left / b.right. 結果為這兩個取max

對于b a 這種排法. b的答案為 pi/ b.right, a 的答案為pi * b.left / a.right 結果為這兩個取max

對于 b a 排法的a 的答案必然大于 a b 排法的a的答案. 所以我們可以忽略 掉a b 的a.

同樣的我們也會忽略 b a 排法的 b.

所以最后就是, pi * a.left / b.right 跟 pi * b.left / a.right 做比較.

?很明顯pi 可以越掉. 剩下就是比較 a.left / b.right?跟 b.left / a.right

移項 得出 a.left * a.right 跟 b.right * b.left 做比較.?

如果我們假設 a b 排法 為優, 那么必須滿足 a.left * a.right <=? b.right * b.left

由於這裡要用大數, 所以用python比較方便

n = int(input()) a = [] t1, t2 = map(int, input().split()) for i in range(n):a.append(list(map(int, input().split()))) a.sort(key = lambda c : c[0] * c[1]); sum = t1 maxn = 0 for i in a:maxn = max(maxn, sum // i[1])sum *= i[0] print(maxn)

NC25043 protecting the flower

這個題也是交換任意兩個相鄰的牛對前面和后面的牛的吃草面積沒有影響.

我們假設ab 這樣的排法更優.

前面的牛所花的總時間為 T, d為吃草速度

那么牛a 所吃的草就是 T * da, 牛b 所吃的草 為 (T + 2 * ta) * db , 由于農夫把牛送回去和回來送b要花2倍的ta.

所以 ab 排法的花費為 T* da + (T+2*ta) *db

對于ba 排法的花費為 T*db?+ (T + 2*tb)*da

ab <= ba, T* da + (T+2*ta) *db <=?T*db?+ (T + 2*tb)*da

化簡得出? T* da + T*db+2*ta*db<=?T*db?+ T *da+ 2*tb*da

ta*db<= tb*da

所以要讓ab <= ba, 就必須滿足 ta / da <= tb/ db, 我們按照這個排序算的結果必然為最好.

NC10712 CF484A Bits

n次詢問, 搵從l 到 r 之間, 哪個數的二進制中最多1.

我們嘗試每次把l 的 二進制中的0 變成1, 看看會不會超過r, 如果不會就繼續加, 會就打印答案.

記住l 有前導0, 所以就算位數不一樣也可以的.

NC1879 毒瘤xor

?xor 操作是取反操作, 假如說在l到r之間 二進制中的 第一位出現0的個數比出現1的個數要多, 那麼第一位顯然要xor 1, 才能確保我們xor的所有數都盡可能的大.?

那麼也就是說如果我們可以統計每一個位數出現0和1的個數, 我們就可以知道我們的x 每一個位數到底是0 還是1.?

只要0 多於1半我們就要幫我們的 x += (1 << j) j就是指定的位數.

顯然要O(1)得到位數的0出現個數 需要用到前綴和. 我們可以用prefixSum[i][j] 表示前i個數中, 第j位有多少個0.?

而最多只會出現r - l + 1 那麼多個0. 所以在查詢的l r 區間中, 只要 (presum[r][j] - presum[l - 1][j]) * 2 < r - l + 1, 就要把x 加上1 << j

#include<iostream> using namespace std; int n, a[100010]; int sum[1e6 + 1][40]; int main(){cin >> n;for(int i = 1; i <= n; i++){cin >> a[i];}for(int j = 0; j <= 30; j++){for(int i = 1; i <= n; i++){sum[i][j] = sum[i - 1][j];if((a[i]>>j) & 1)sum[i][j]++;}}int q;cin >> q;for(int i = 1; i <= q; i++){int l, r;cin >> l >> r;int x = 0;for(int j = 0; j < 31; j++){//to check if the amount of 0 is above half or not, if it is we xor this pos with 1, to turn all of them //1if((sum[r][j] - sum[l - 1][j])*2 < r - l + 1){x|=(1<<j);}}cout << x << endl;}return 0; }

NC20860 兔子的區間密碼

要找出l 到 r區間中 任何兩個數xor後的最大值..

假如 l = 101000 r = 101100

只要從最高位開始數起他們的二進制表達式都是一樣的, 那麼在l到r這個區間裡的所有數字他們從最高位開始數起他們的二進制表達式都是一樣的.?

所以如果我們要考慮選兩個數字他們的xor最大, 那麼只要從最高位開始走起, 只要遇到一個位他們是不一樣的, 我們就可以直接把後面所有數字變成1, 那麼就會是 xor後的結果.

假如 l = 101000 r = 101100

從最高位第4位開始不一樣, 我們永遠能選擇和 r 相反的數字, 而且可以保證不會數值不會低於l.

所以選擇的兩個數字為 101011 和 101100, xor後的結果為 111.

#include <bits/stdc++.h> using namespace std; #define int long longint kpow(int a,int n) {int ans=1;while(n) {if(n&1)ans*=a;a*=a;n>>=1;}return ans; }signed main() {int t,a,b;cin>>t;while(t--) {cin>>a>>b;int ans=0;for(int i=63;i>=0;i--)if((a>>(i-1))!=(b>>(i-1))) {ans=kpow(2,i)-1;break;}cout<<ans<<'\n';}return 0; }

起床困難綜合癥

由於每一個位數到最後的結果都是分開和獨立的, 所以我們第一個方案就是考慮, 用0 去走一遍看看最後結果是什麼, 再用1去走一遍看看結果是什麼, 如果其中一個結果是1, 那麼我們就取1, 我們把m的最大值 111111111111111111111..... , 和 最小值 00000000000000000...., 都走一遍最後有1的看看他初始攻擊力是0 還是 1, 然後跟著取回原來的數就可以了.

由於題目希望 初始攻擊力盡可能小, 所以我們優先把 0 可以變 1 的考慮.?

其次 1 最後經過m 個門也是1 的, 我們也會考慮要1. 但是同時 也要考慮 我們的初始攻擊力不會超過m的範圍 就是 1e10, 10000000000, 我們的答案有可能會是 10001000000, 這些的, 所以我們每取了一個位數, 我們就把m 減掉這個數字, 就可以保證我們不會超過m的範圍.

#include<bits/stdc++.h> using namespace std; #define ll long long int main() {int n,m;cin >> n >> m;int ans=0,ans2=-1;for(int i=1;i<=n;i++){string s;cin >> s;int t;cin >> t;if(s[0]=='O') ans|=t,ans2|=t;if(s[0]=='X') ans^=t,ans2^=t;if(s[0]=='A') ans&=t,ans2&=t;}int res=0;for(int i=30;i>=0;i--){if(ans>>i&1) res+=(1<<i);else if(ans2>>i&1&&(1<<i)<=m) res+=(1<<i),m-=(1<<i);}cout << res << endl; }

總結

以上是生活随笔為你收集整理的牛客算法課 (算法入門班) 貪心與模擬(4)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。