贪心上机训练题解
终于我把贪心的上机训练做完了,现在就把这六道题说一下
第一题 数列极差
贪心策略:
从小到大排序,每次擦掉最大的两个数,然后剩下的就是最小的
从小到大排序,每次擦掉最小的两个数,然后剩下的就是最大的
稍微提示一下,我们每次都要擦掉最小的两个数,所以每一遍都要拍一遍序
并且:千万别排成别的数组
知道了这样的贪心策略,程序也很好写出来
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,a[50050],b[50050],c[50050];
int minn,maxx;
void ask_min(){
for(int i=1;i<n;i++){
b[i+1]=b[i]*b[i+1]+1;
sort(b+1,b+n+1);
}
minn=b[n];
}
void ask_max(){
for(int i=n;i>1;i--){
c[i-1]=c[i]*c[i-1]+1;
sort(c+1,c+i);
}
maxx=c[1];
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
c[i]=a[i];
}
sort(c+1,c+n+1);
sort(b+1,b+n+1);
ask_min();
ask_max();
printf("%d",minn-maxx);
return 0;
}
第二题 数列分段
贪心策略:
每段尽可能接近m
code:
#include<bits/stdc++.h>
using namespace std;
int m,n,sum,ans;
int a[100010];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
sum+=a[i];
if(sum==m){
ans+=1;
sum=0;
continue;
}
if(sum>m){
ans+=1;
i--;
sum=0;
continue;
}
if(sum<m&&i==n){
ans+=1;break;
}
}
printf("%d",ans);
return 0;
}
第三题 线段
例题一...
code:
#include<bits/stdc++.h>
using namespace std;
int n,record,ans;
struct Node{
int b,e,w;
}a[1000010];
bool cmp(Node x,Node y){
if(x.e==y.e) return x.w<y.w;
return x.e<y.e;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].b,&a[i].e);
a[i].w=a[i].e-a[i].b;
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++){
if(record<=a[i].b){
record=a[i].e;
ans++;
}
}
cout<<ans;
return 0;
}
第四题 家庭作业
例题五...
但是
我把代码复制过去竟然没过,于是稍微优化了一下
最开始,我打了个快读,反而更慢了(难道说字符串还没有scanf优秀?)
所以就开始从贪心实现这方面思考,怎么才能优化
搜索有剪枝,剪掉多余的,无用的循环、搜索
我就让贪心也剪掉多余的循环
用一个dis来储存现在究竟到了哪一天(前面的天数都不能再做作业了)
这样当后面的再有x[i].a < dis 的就continue了
减少了进入下一个循环的次数
于是就成功的过了这道题
code:
#include<bits/stdc++.h>
using namespace std;
int n,ans,dis;
struct Node{
int a,b;
}x[1000005];
bool cmp(Node xx,Node yy){
if(xx.b==yy.b) return xx.a<yy.a;
return xx.b>yy.b;
}
#define rg register
inline int read(){
rg int s=0,f=0;
rg char ch=getchar();
while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
while(isdigit(ch)) s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
return f?-s:s;
}
bool fk[1000005];
int main(){
// scanf("%d",&n);
n=read();
for(rg int i=1;i<=n;i=-~i){
x[i].a=read();
x[i].b=read();
}
sort(x+1,x+n+1,cmp);
for(rg int i=1;i<=n;i=-~i){
if(x[i].a<dis) continue;
int bj=1;
for(rg int j=x[i].a;j;--j){
if(!fk[j]){
fk[j]=1;
bj=0;
ans+=x[i].b;
break;
}
}
if(bj==1) dis=x[i].a;
}
printf("%d",ans);
return 0;
}
第五题 钓鱼
这道题一开始我是真心不会,问的本机房的一位神犇,他说这是一道DP
但是DP我还没学,而且书上给的上机训练的的确确说这道题是贪心。
转过头去,问另一位大佬,他用了几分钟时间就想出了贪心思路
直接交,立马AC
所以,贪心还是需要多做题,这样才能立刻想出来。
贪心思路:
我们可以把每次要去的最远的鱼塘模拟一遍,之后就知道了到每个鱼塘所需的走路时间
如此之后,它的走路消耗便可以不算了
题目就变成了:有n个鱼塘,到每个的距离都是0
怎样在每个鱼塘钓鱼...
这样它的贪心策略也就变成了:每次看哪一个鱼塘里的与最多。
code:
#include<bits/stdc++.h>
using namespace std;
int n,h,t[105],ans,c[105],sum;
struct Node{
int a,m;
}x[105];
struct Nod{
int a,m;
}y[105];
bool cmp(Nod x,Nod y){
return x.a>y.a;
}
int main(){
scanf("%d%d",&n,&h);
h*=12;
for(int i=1;i<=n;i++) scanf("%d",&x[i].a);
for(int i=1;i<=n;i++) scanf("%d",&x[i].m);
for(int i=2;i<=n;i++) scanf("%d",&t[i]);
t[1]=0;
for(int i=1;i<=n;i++){
h-=t[i];
ans=0;
for(int j=1;j<=i;j++){
y[j].a=x[j].a;
y[j].m=x[j].m;
}
for(int j=1;j<=h;j++){
sort(y+1,y+i+1,cmp);
if(y[1].a<0) continue;
ans+=y[1].a;
y[1].a-=y[1].m;
}
if(ans>sum) sum=ans;
// cout<<ans<<"\n";
}
// for(int j=1;j<=n;j++){
// cout<<y[j].a<<" ";
// }
printf("%d",sum);
return 0;
}
第六题 糖果传递
看这题第一眼:这不均分纸牌吗?
再多看几眼,这题咋整?
数学:
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
long long sum,n,need,ans;
long long y[1000005],a[1000050],x[1000050];
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
sum+=a[i];
}
need=sum/n;
sum=a[1];
for(int i=2;i<=n;i++){
y[i]=-((i-1)*need-sum);
sum+=a[i];
}
sort(y+1,y+n+1);
memset(a,0,sizeof(a));
x[1]=y[(n+1)/2];
// cout<<x[1]<<" sdf;laj\n";
for(int i=1;i<=n;i++){
// cout<<x[i]<<" ";
a[i]=x[1]-y[i];
// cout<<a[i]<<" ";
}
// for(int i=1;i<=n;i++) cout<<x[i]<<" ";
for(int i=1;i<=n;i++) ans+=abs(a[i]);
printf("%lld",ans);
return 0;
}