二分答案
最佳牛围栏
代码:
#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 后就变为 这一段的和是否大于0
for(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;
else
pos = n >>1;
ans = 0;
for(int i = 1;i<= n;++i)
ans += abs(a[i] - a[pos]);
printf("%lld\n",ans);
return 0;
}
动态中位数
解题思路:
技巧:
- 大根堆与小根堆
priority_queue<int> down; //大根堆
priority_queue<int, vector<int>, greater<int>> up; //小根堆
- 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
<
j
a
n
d
a
[
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++];
else
b[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++];
else
b[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;
else
a[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;
else
a[i-ok] = x;
}
ans = 0,msort(1,n*n);
//printf("%d %d\n",cnt,ans);
if((cnt & 1) == (ans & 1) ) // 记得加括号 , & 运算 比 == 都小
printf("TAK\n");
else
printf("NIE\n");
}
return 0;
}