二分答案,离散化等
二分答案,離散化等
- 二分答案
- 最佳牛圍欄
- 離散化
- 電影
- 中位數
- 貨倉選址
- 動態中位數
- 逆序對
- 利用歸并排序求
- 奇數碼問題
- 當n是奇數時
- 當n是偶數時
二分答案
最佳牛圍欄
題目鏈接
解題思路
代碼:
#include<stdio.h> #include<iostream> #include<math.h> #include<algorithm> using namespace std; const int N = 1e5 + 10; const double eps = 1e-5; double a[N],b[N],sum[N]; int n,f; int main(){scanf("%d%d",&n,&f);for(int i = 1 ;i<=n;++ i)scanf("%lf",&a[i]);double L = -1e6 , r = 1e6;// 二分平均數while(L + eps < r ){double mid = (L + r)/2;// 這一段長度中的平均數是否大于 mid ,都減去mid 后就變為 這一段的和是否大于0for(int i = 1;i<= n ;++i) b[i] = a[i] - mid;// 求前綴和for(int i = 1;i<=n;++i) sum[i] = sum[i-1] + b[i];double ans = -1e10;double min_val = 1e10;for(int i = f;i<=n;++i){min_val = min(min_val,sum[i-f]);ans = max(ans,sum[i] - min_val);}if(ans >=0) L = mid; else r = mid; }cout<<int(r*1000)<<endl;return 0; }離散化
電影
題目鏈接
解題思路:
這道題目是一道離散化的好題,因為我們這里的語言,是非常的分散,所以我們可以把他們匯聚在一起,也就是重新定義這些語言,重新標號1,2,3,…,n1,2,3,…,n。
這道題目統計方面,因為時間限制非常嚴格,所以動用了C++11的哈希級別map,
代碼:
#include<iostream> #include<stdio.h> #include<map> #include<algorithm> using namespace std;const int N = 2e5 + 10;int n,m; map<int,int>lg; int a[N] ,b[N] ,ans[N];int main() {scanf("%d",&n);for(int i= 1;i<= n ;++i){int x;scanf("%d",&x);lg[x]++;}scanf("%d",&m);for(int i=1;i<=m;++i)scanf("%d",&a[i]);for(int i=1;i<=m;++i)scanf("%d",&b[i]);int maxx = 0;for(int i=1;i<=m;++i)maxx = max(maxx,lg[a[i]]); //得到會語言最多的人數int k = 0;for(int i=1;i<=m;++i)if(lg[a[i]] == maxx) // 通過這個人數去 找能開心里邊電影的編號存到數組中ans[k++] = i;if(k == 1) // 如果只有一場,直接輸出printf("%d\n",ans[0]);else{int res = 0,pos = 0;for(int i = 0;i<k;++i) // 否則在這幾個電影中 ,找到交開心的電影編號,如果相同選后面的if(lg[b[ans[i]]] >= res){ res = lg[b[ans[i]]];pos = ans[i];}printf("%d\n",pos);}return 0; }中位數
貨倉選址
題目鏈接
解題思路;
排序+中位數
中位數有非常優秀的性質,比如說在這道題目中,每一個點到中位數的距離,都是滿足全局的最有性,而不是局部最優性。
具體的來說,我們設在倉庫左邊的所有點,到倉庫的距離之和為pp,右邊的距離之和則為qq,那么我們就必須讓p+qp+q的值盡量小。
當倉庫向左移動的話,pp會減少xx,但是qq會增加n?xn?x,所以說當為倉庫中位數的時候,p+qp+q最小。
還是同樣的一句話,畫圖理解很重要。
首先看兩個點的情況
類推到n
代碼:
#include<stdio.h> #include<algorithm>using namespace std;const int N = 1e5 + 10; int n,a[N]; long long ans;int main(){scanf("%d",&n);for(int i = 1; i <= n;++i)scanf("%d",&a[i]);sort(a+1,a+1+n);int pos ;if(n&1)pos = (n+1)>>1;elsepos = n >>1;ans = 0;for(int i = 1;i<= n;++i)ans += abs(a[i] - a[pos]);printf("%lld\n",ans);return 0; }動態中位數
題目鏈接
解題思路:
技巧:
- 大根堆與小根堆
- n個數中位置為奇數的個數與中間的位置。
統一可以寫成:(n+1)>>1
代碼:
#include<stdio.h> #include<algorithm> #include<vector> #include<queue>using namespace std;int t,T,id,n; // 以大根堆的頂部作為中位數 int main(){scanf("%d",&T);while(T--){scanf("%d%d",&id,&n);printf("%d %d\n",id,(n+1)>>1);int cnt = 0;priority_queue<int> down; //大根堆priority_queue<int, vector<int>, greater<int>> up; //小根堆for(int i=1;i<=n;++i){int x;scanf("%d",&x);// 大根堆為空,或者當前值小于等于中位數,插入大根堆if(down.empty() || x<= down.top()) down.push(x);else up.push(x);//否者插入小根堆// 因為是大根堆堆頂是中位數,正常情況下 大根堆數量應為 小根堆數量 + 1// 所以 如果 大于 up.size() + 1 ,將頂部放到小根堆上if(down.size() > up.size() + 1){up.push(down.top()),down.pop() ;}// 而小跟堆是小于1個,所以以 等于的話就算是多了if(up.size() > down.size()) { down.push(up.top()) , up.pop(); }if(i&1){printf("%d ",down.top());if(++cnt % 10 == 0)putchar('\n');}}if(cnt %10)putchar('\n');}return 0; }逆序對
定義
對于一個序列a,若
i<janda[i]>a[j],則稱a[i]與a[j]構成逆序對i<j\ and \ a[i] > a[j] ,則稱 a[i] 與 a[j] 構成逆序對 i<j?and?a[i]>a[j],則稱a[i]與a[j]構成逆序對
利用歸并排序求
代碼:
// left = 1, right = n; void msort(int left , int right){if(left == right)return ;int mid = (left + right) >>1,i,j,k;msort(left,mid),msort(mid+1,right);k = left;for( i = left,j = mid+1;i<=mid && j<=right;){if(a[i]<=a[j])b[k++] = a[i++];elseb[k++] = a[j++] , ans += mid - i + 1; // 表明后半部分有比前半部分小的// 其中的個數 就是 i 為前半部分在比較的,即是它大于后半部分// 個數就是 它 到中間位置的距離。}while(i<=mid)b[k++] = a[i++];while(j<=right) b[k++] = a[j++],ans += mid-i+1;// printf("ans %d \n",ans);for( i = left;i<=right;++i)a[i] = b[i]; }奇數碼問題
題目鏈接
這類題目有結論:
當n是奇數時
當且僅當兩個局面下網格中的數依次寫成一行 n*n-1 個元素的序列后,不考慮空格,逆序對的奇偶性相同就可以。
當n是偶數時
當且僅當兩個局面下網格中的數依次寫成一行 n*n-1 個元素的序列后,不考慮空格,“逆序對數之差” 和 " 兩個局面下空格所在的行數之差" 奇偶性相同時可以。
代碼:
#include<stdio.h> #include<algorithm> #include<cstring> const int N = 3e5 + 10;int n,a[N],b[N]; long long cnt1 , cnt , ans;void msort(int left , int right){if(left == right)return ;int mid = (left + right) >>1,i,j,k;msort(left,mid),msort(mid+1,right);k = left;for( i = left,j = mid+1;i<=mid && j<=right;){if(a[i]<=a[j])b[k++] = a[i++];elseb[k++] = a[j++] , ans += mid - i + 1; // 表明后半部分有比前半部分小的// 其中的個數 就是 i 為前半部分在比較的,即是它大于后半部分// 個數就是 它 到中間位置的距離。}while(i<=mid)b[k++] = a[i++];while(j<=right) b[k++] = a[j++],ans += mid-i+1;// printf("ans %d \n",ans);for( i = left;i<=right;++i)a[i] = b[i]; }int main(){while(~scanf("%d",&n)){int ok = 0,x; // 不把0加進去for(int i=1;i<=n*n;++i){ scanf("%d",&x);if(!x)ok = 1;elsea[i-ok] = x;}ans = 0 , memset(b,0,sizeof b);msort(1,n*n);cnt = ans , memset(b,0,sizeof b),ok = 0;memset(a,0,sizeof a); // 因為0在的位置可能不一樣所以要清空for(int i=1;i<=n*n;++i){ scanf("%d",&x);if(!x)ok = 1;elsea[i-ok] = x;}ans = 0,msort(1,n*n);//printf("%d %d\n",cnt,ans);if((cnt & 1) == (ans & 1) ) // 記得加括號 , & 運算 比 == 都小printf("TAK\n");elseprintf("NIE\n");}return 0; }總結
- 上一篇: 基本算法之前缀和与差分的是使用
- 下一篇: 基本算法之贪心算法