牛客算法課 (算法入門班) 貪心與模擬(4)
目錄
相差最大(貪心策略列子1)
[NOIP1998]拼數(shù)
區(qū)間覆蓋
NC16561 國王的游戲
NC25043 protecting the flower
NC10712 CF484A Bits
NC1879 毒瘤xor
NC20860 兔子的區(qū)間密碼
起床困難綜合癥
對於任何二進制題目, 我們都要考慮是否可以每一位單獨考慮!
相差最大(貪心策略列子1)
給定長度為n的整數(shù)數(shù)列{Ai}, 找出兩個整數(shù) ai 和 aj (i < j), 使得ai - aj盡量的大.
暴力一定是固定一個點ai, 枚舉aj, 然後打擂臺.
你會發(fā)現(xiàn)如果我們把後綴最小的aj 提前算出來, 我們就可以省略一個nested loop去找最小aj了.
又或者你固定aj, 去找前綴最大ai.
思維講解 沒有代碼
[NOIP1998]拼數(shù)
一開始直接有一個思路就是按照字典序排序, 然後從大到小合拼, 但是, 由於每個數(shù)字的長度不一樣, 所以有機會出錯, 我們可以考慮, 比較的時候, 讓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; }區(qū)間覆蓋
給出n個區(qū)間, 讓你找出最多的區(qū)間,并且他們不想交.
可以利用貪心, 把他按照右端點進行排序, 因為只要一個區(qū)間的右端點靠前, 那么右端點比他大的區(qū)間能與靠后的區(qū)間成為一個子集, 那么靠前的那個區(qū)間也一定行. 利用這個思想就可以輕松把題目做出來.
NC16561 國王的游戲
套路題, 如果任意兩個相鄰的人交換不會影響后面的人和前面的人的結(jié)果, 我們可以進行推論.
設前面的人的乘積是pi. ab 為相鄰的兩個人.?
對于 a b 這種排法. a的答案為 pi / a.right?, b 的答案為 pi * a.left / b.right. 結(jié)果為這兩個取max
對于b a 這種排法. b的答案為 pi/ b.right, a 的答案為pi * b.left / a.right 結(jié)果為這兩個取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 排法 為優(yōu), 那么必須滿足 a.left * a.right <=? b.right * b.left
由於這裡要用大數(shù), 所以用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 這樣的排法更優(yōu).
前面的牛所花的總時間為 T, d為吃草速度
那么牛a 所吃的草就是 T * da, 牛b 所吃的草 為 (T + 2 * ta) * db , 由于農(nóng)夫把牛送回去和回來送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, 我們按照這個排序算的結(jié)果必然為最好.
NC10712 CF484A Bits
n次詢問, 搵從l 到 r 之間, 哪個數(shù)的二進制中最多1.
我們嘗試每次把l 的 二進制中的0 變成1, 看看會不會超過r, 如果不會就繼續(xù)加, 會就打印答案.
記住l 有前導0, 所以就算位數(shù)不一樣也可以的.
NC1879 毒瘤xor
?xor 操作是取反操作, 假如說在l到r之間 二進制中的 第一位出現(xiàn)0的個數(shù)比出現(xiàn)1的個數(shù)要多, 那麼第一位顯然要xor 1, 才能確保我們xor的所有數(shù)都盡可能的大.?
那麼也就是說如果我們可以統(tǒng)計每一個位數(shù)出現(xiàn)0和1的個數(shù), 我們就可以知道我們的x 每一個位數(shù)到底是0 還是1.?
只要0 多於1半我們就要幫我們的 x += (1 << j) j就是指定的位數(shù).
顯然要O(1)得到位數(shù)的0出現(xiàn)個數(shù) 需要用到前綴和. 我們可以用prefixSum[i][j] 表示前i個數(shù)中, 第j位有多少個0.?
而最多只會出現(xiàn)r - l + 1 那麼多個0. 所以在查詢的l r 區(qū)間中, 只要 (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 兔子的區(qū)間密碼
要找出l 到 r區(qū)間中 任何兩個數(shù)xor後的最大值..
假如 l = 101000 r = 101100
只要從最高位開始數(shù)起他們的二進制表達式都是一樣的, 那麼在l到r這個區(qū)間裡的所有數(shù)字他們從最高位開始數(shù)起他們的二進制表達式都是一樣的.?
所以如果我們要考慮選兩個數(shù)字他們的xor最大, 那麼只要從最高位開始走起, 只要遇到一個位他們是不一樣的, 我們就可以直接把後面所有數(shù)字變成1, 那麼就會是 xor後的結(jié)果.
假如 l = 101000 r = 101100
從最高位第4位開始不一樣, 我們永遠能選擇和 r 相反的數(shù)字, 而且可以保證不會數(shù)值不會低於l.
所以選擇的兩個數(shù)字為 101011 和 101100, xor後的結(jié)果為 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; }起床困難綜合癥
由於每一個位數(shù)到最後的結(jié)果都是分開和獨立的, 所以我們第一個方案就是考慮, 用0 去走一遍看看最後結(jié)果是什麼, 再用1去走一遍看看結(jié)果是什麼, 如果其中一個結(jié)果是1, 那麼我們就取1, 我們把m的最大值 111111111111111111111..... , 和 最小值 00000000000000000...., 都走一遍最後有1的看看他初始攻擊力是0 還是 1, 然後跟著取回原來的數(shù)就可以了.
由於題目希望 初始攻擊力盡可能小, 所以我們優(yōu)先把 0 可以變 1 的考慮.?
其次 1 最後經(jīng)過m 個門也是1 的, 我們也會考慮要1. 但是同時 也要考慮 我們的初始攻擊力不會超過m的範圍 就是 1e10, 10000000000, 我們的答案有可能會是 10001000000, 這些的, 所以我們每取了一個位數(shù), 我們就把m 減掉這個數(shù)字, 就可以保證我們不會超過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; }總結(jié)
以上是生活随笔為你收集整理的牛客算法課 (算法入門班) 貪心與模擬(4)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: nacos忘记密码
- 下一篇: 凡事不可过....久视伤血,久卧伤气,久