送分题
直接开个数组表示第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);
}
模拟题
我们首先分析这个人的移动规律:
先往右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);
}
首先讲暴力
从后往前枚举树的最大高度
出现第一个砍树的高度>=所需的木材数即可
注意到这个答案是有单调性的
所以我们考虑二分
二分砍树的最大高度
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);
}
题目要我们求什么呢?
希望求出我们能取得的最多的钱
最多的钱是什么意思?
扣得钱最少嘛
怎么样扣得钱可以最少呢?
那就是尽量先把扣钱多的项目先做掉嘛
好
这就是这道题贪心的第一个思路:
优先把扣钱多的项目做掉
所以我们需要先把项目按照扣钱数从大到小进行排序
在前面的项目尽可能先做
那么对于一个项目,我们要怎么去做才能做到最优呢?
是在靠后的时间点去做,还是在靠前的时间点去做呢?
对,就是在靠后的时间点去做。
为什么呢?
我们在如果在靠后的时间点把这个项目做掉了,那么我们在前面的时间点就有可能去完成其他的项目
不然,如果我们在前面就把这个项目做掉,就可能导致其他截止时间小的项目无法完成
对吧?
好
这就是我们的第二个贪心思路:
对于一个游戏,我们要在尽可能靠后的时间点去完成,这样才会给其他项目留下更多的额可能
#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);
}