牛客暑期训练第5场部分题解

H Holding Two

题意:

给出n,m,要求用1和0构造一个 n*m 的矩阵,使得其中不存在三个相同的数在一条线上(包括横线、竖线、斜线),若能构造,输出这个矩阵,不能输出-1。

思路:

构造得到了一个满足条件的矩阵

0011001100……
1100110011……
0011001100……
1100110011……
………………
其长和宽均可无限循环延长,故对于任意的m和n,输出该矩阵的一部分即可。

代码:

#include<iostream>
using namespace std;
int a[1020][1020];
int main(){
	int n,m;
	cin>>n>>m;
	/*if(n<3||m<3){
		printf("")
	}*/
	
	a[0][0]=0;a[0][1]=0;a[0][2]=1;a[0][3]=1;
	for(int i=4;i<1005;i++){
		a[0][i]=a[0][i-4];
	}
	for(int i=1;i<1005;i++){
		for(int j=0;j<1005;j++){
			a[i][j]=1-a[i-1][j];
		}
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			cout<<a[i][j];
		}
		cout<<endl;
		
	}
}

B Boxes

题意:

一共有n个盒子,每个盒子里面装黑球和白球的概率各为1/2,各个盒子间相互独立,打开一个盒子需要支付Wi的代价。你还可以选择支付C,来知道总共有多少个黑球。求弄清每一个盒子里球的颜色需要支付的最小代价的期望。

思路:

分为两种情况。第一种不支付C,打开所有盒子,总代价为\sum Wi

第二种先支付C,此时我们已知了有k个黑球,将w排序后依次打开盒子,直到剩下的盒子里都是黑球或者白球时,停止。从反方向考虑,也就是一组随意数后 i 个都是同一颜色的概率 * 打开前 n-i 个盒子需要的总代价, 最后对i累和即可。

代码:

#include<bits/stdc++.h>
using namespace std;

const int N=1e6+10;

int n;
double a[N], sum[N];
double res, c;

int main(){
	cin>>n>>c;
	for(int i=1; i<=n; ++i){
		scanf("%lf", &a[i]);
	}
	sort(a+1,a+n+1);
	for(int i=1; i<=n; ++i){
		sum[i]=sum[i-1]+a[i];
	}
	res=c;
	double p=1;
	for(int i=1; i<n; ++i){
		p/=2.0;
		res+=sum[n-i]*p;
		//res+=sum[n-i]*(1.0/(1<<i));
	}
	double ans=min(res,1.0*sum[n]);
	printf("%.10lf", ans);
}

K King of Range

题意:

给定 n 个整数 a[n]​ 和 m次查询。对于每个查询,都会给您一个常量k,求有多少不同的(l,r)​ 满足子序列 a[l] ~ a[r] 的中最大值减去最小值大于k。

思路:

 尺取。对于满足条件的(l ,r),易知右端点取在 r 后的值也能满足,r 搜索到这一点后停止,ans+=n-r+1。l 在往后移动一位的时候,满足条件的结尾一定在r 点或之后,所以r 的起点还是当前位置,然后继续按照第一步继续,直到统计完成。

 需要记录每一次改变l 和r 后区间的最小值和最大值,可以用双端队列实现。

代码:

#include <cstdio>
#include <iostream>
#include <deque>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxN = 1e5 + 7;
typedef long long ll;

int m, n, a[maxN];
int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++i)
		scanf("%d", &a[i]);
	for(int i = 1; i <= m; ++i) {
		int k;
		scanf("%d", &k);
		deque<int> maxPos, minPos; //双端队列维护当前最大值和最小值 (maxPos.front() ) 
		int l = 1,r = 0; //所取区间的起点l,终点r 
		ll ans = 0;
		while(l <= n && r <= n) {
			while((!maxPos.empty()) && maxPos.front() < l)
				maxPos.pop_front();//前一个数在起点之前,删去
			while((!minPos.empty()) && minPos.front() < l)
				minPos.pop_front();
			if((!maxPos.empty()) && a[maxPos.front()] - a[minPos.front()] > k) {
				ans += n - r + 1; //选取之后的点作为结尾,也满足条件 
				++l;
				continue;
			} else {
				++r;
				while(!maxPos.empty() && a[maxPos.back()] <= a[r])
					maxPos.pop_back();
				maxPos.push_back(r);//更改最大值 
				while(!minPos.empty() && a[minPos.back()] >= a[r])
					minPos.pop_back();
				minPos.push_back(r);//更改最小值 
			}
		}
		printf("%lld\n", ans);
	}
	return 0;
}

总结:

这场比赛过了一题。

H题是构造,几次尝试之后成功AC。然后开了B题和K题。K题想到了是尺取,但是不知道怎样记录每次变化后的最小值和最大值,时间复杂度降不下来。题解用的双端队列,很妙0v0。以后对STL的运用上要加强。B题没有想到反面思考,用的是递归还有二项分布,TLE了三次……

这场下来总体的感觉是数学没学好,想的太复杂了,两题都是TLE。以后要好好学习,重新做人。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值