【滨小】模拟赛题解

T 1 T1 T1

送分题
直接开个数组表示第i个栅栏是否被涂过
循环两遍将涂过的栅栏标记
最后累加一遍即可

#include
using namespace std;

bool f[10100];
int a,b,c,d;
int ans = 0;

int main(){
	scanf("%d %d\n%d %d",&a,&b,&c,&d);
	for (int i = a; i <= b; i++) f[i] = 1;
	for (int i = c; i <= d; i++) f[i] = 1;
	for (int i = 0; i <= 100; i++) ans+=f[i];
	printf("%d",ans);
}

T 2 T2 T2

模拟题
我们首先分析这个人的移动规律:
先往右1,在往左2,在往右4……

我们就会发现这道题有两个关键点:
一是方向,这个人是先往右再往左,是有两个方向
另一个人在这个人的左边或者右边都有可能
这是第一个要注意的地方

第二个就是距离
我们注意到这个距离的增加是有规律的
1,2,4,8……
是不断*2的

总结出这两个点之后我们就可以有思路了:
首先我们用一个布尔变量去记录方向,0/1分别表示往左/往右
再用一个变量去记录当前移动的距离
当我们行走的方向与这个人对我们的方向一致

当前行走的距离大于等于两人之间的距离时
就是找到的时候
中间把行走的距离再加上即可

#include
using namespace std;

int l,r;
int ans = 0;
int k = 1;
bool f = 1,F = 0;

int main(){
	cin>>l>>r;
	if (l == r){printf("0");return 0;}
	F = (l<r);
	while (1){
		if (f == F && k >= abs(r-l)) {ans+=k/2+abs(r-l);break;}
		ans+=k/2+k;
		k*=2; f=!f;
	}
	printf("%d",ans);
}

T 3 T3 T3

一、暴力

首先讲暴力
从后往前枚举树的最大高度
出现第一个砍树的高度>=所需的木材数即可

二分

注意到这个答案是有单调性的
所以我们考虑二分
二分砍树的最大高度
Check一遍即可

#include
using namespace std;

const int N = 1e5+10;
int n,m;
int a[N];

bool Check(int x){
	long long s = 0;
	for (int i = 1; i <= n; i++) s+=((a[i]>x)?a[i]-x:0);
	return s>=m;
}

int main(){
	int Max = 0;
	scanf("%d %d",&n,&m);
	for (int i = 1; i <= n; i++) scanf("%d",&a[i]) , Max = max(Max,a[i]);
	int l = 1 , r = Max;
	while (l+1<r){
		int Mid = (l+r)>>1;
		if (Check(Mid)) l = Mid;
		else r = Mid;
	}
	printf("%d",Check(r)?r:l);
	return 0;
}

我们将树的高度分成不同的区间段
不同区间段内砍1cm树的所获得的木头数量是不一样的
什么意思呢?
比如说我们现在有以下高度的树:
10 13 18 21 23
我们将高度分成以下区间段:
1、>23,此时砍树1cm,获得的木材数量为0(因为不需要砍)
2、21~23,此时砍树1cm,获得的木材数量为1(只需要砍最高的那颗树)
3、18~21,此时砍树1cm,获得的木材数量为2(需要砍最高的两棵树)
4、13~18,同理,此时砍树1cm,获得的木材数量为3
5、10~13,此时砍树1cm,获得的木材数量为4
6、<10,此时砍树1cm,获得的木材数量为5

我们这个时候注意到一个规律:
每次越过一个树的高度,他砍树1cm获得木材的数量就+1

于是我们有了如下思路:
从后往前枚举树的高度
利用一个变量记录砍1cm能获得的木材数量
每次砍1cm,就需要累加上相应的木材数量
当当前高度存在一颗树时,我们的单位厘米木材数量就+1
当累计木材数量>=m时,当前高度就是我们所求

如何知道当前高度是否有树呢?
我们利用一个桶,去记录相应高度是否有树即可

#include
using namespace std;

int n,a[10000001]={},maxx=0,x=0,s=0,ans=0,k=0,ss=0;

int main(){
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++){
		scanf("%d",&x);
		maxx=max(maxx,x);
		a[x]++;
    }
	for (ans=maxx;ss<k;ans--){
		s+=a[ans];
		ss+=s;
	}
	cout<<ans;
	return 0;
}

对桶的再优化

我们将单位cm获得的木材数量简称为代价
我们发现当我们越过一棵树的高度的时候,代价就会+1

能不能不用桶呢?
当然可以

我们用一个数组 a [ i ] a[i] a[i]表示第 i i i棵树的高度
首先将他从大到小排序
然后我们发现:
i i i棵树和第 i + 1 i+1 i+1棵树高度之间的代价为 i i i
那么如果将第 i i i棵树和第 i + 1 i+1 i+1棵树之间的高度全部砍掉,他会获得多少木材呢?
i ∗ ( a [ i ] − a [ i + 1 ] ) i*(a[i]-a[i+1]) i(a[i]a[i+1])个数量的木材

我们一遍循环进行对木材数进行累加
如果加上当前区间的全部木材数大于m
说明最大高度在这个区间
此时我们记录一下剩余所需木材数
木材数 / i 木材数/i 木材数/i就是这个区间需要砍掉的高度
也就得到了答案

#include
using namespace std;

int n,m;
int a[101000];
int Max = 0;
int ans = 0,su = 0;

bool cmp(int x,int y){
	return x>y;
}

int main(){
	scanf("%d %d",&n,&m);
	for (int i = 1; i <= n; i++) scanf("%d",&a[i]),Max = max(Max,a[i]);
	sort(a+1,a+n+1,cmp);
	if (m == 0) {printf("%d",Max);return 0;}
	for (int i = 1; i < n; i++){
		if (su+i*(a[i]-a[i+1])>=m){
			m-=su;
			ans=a[i]-((m%i==0)?m/i:m/i+1);
			break;
		}
		su+=i*(a[i]-a[i+1]);
	}
	printf("%d",ans);
}

T4

题目要我们求什么呢?
希望求出我们能取得的最多的钱

最多的钱是什么意思?
扣得钱最少嘛

怎么样扣得钱可以最少呢?
那就是尽量先把扣钱多的项目先做掉嘛


这就是这道题贪心的第一个思路:
优先把扣钱多的项目做掉

所以我们需要先把项目按照扣钱数从大到小进行排序
在前面的项目尽可能先做

那么对于一个项目,我们要怎么去做才能做到最优呢?
是在靠后的时间点去做,还是在靠前的时间点去做呢?

对,就是在靠后的时间点去做。
为什么呢?
我们在如果在靠后的时间点把这个项目做掉了,那么我们在前面的时间点就有可能去完成其他的项目

不然,如果我们在前面就把这个项目做掉,就可能导致其他截止时间小的项目无法完成
对吧?


这就是我们的第二个贪心思路:
对于一个游戏,我们要在尽可能靠后的时间点去完成,这样才会给其他项目留下更多的额可能

#include
using namespace std;

int m,n;
struct Node{
	int t,w;
}a[101001];
bool f[101000];

bool cmp(Node a,Node b){
	return a.w>b.w;
}

int main(){
	scanf("%d\n%d",&m,&n);
	for (int i = 1; i <= n; i++) scanf("%d",&a[i].t);
	for (int i = 1; i <= n; i++) scanf("%d",&a[i].w);
	sort(a+1,a+n+1,cmp);
	for (int i = 1; i <= n; i++){
		bool F = 0;
		int T = a[i].t;
		while (f[T] && T) T--;
		if (T == 0) m-=a[i].w;
		else f[T] = 1; 
	}
	printf("%d",m);
}

你可能感兴趣的:(滨小之旅)