YbtOJ 「基础算法」第2章 贪心算法
例题1.奶牛晒衣服
注意:烘干 \(b\) 是指在每分钟烘干 \(a\) 的基础上额外增加 \(b\) !!1(太坑人了
二分答案,check 写法显然。
code
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int n,h[N],a,b;
int maxn;
bool check(int x)
{
if(b<0)
{
for(int i=1;i<=n;i++) if(a*x<h[i]) return 0;
return 1;
}
int sum=0;
for(int i=1;i<=n;i++)
{
if(a*x<h[i]) sum+=((h[i]-a*x)+b-1)/b;
}
if(sum<=x) return 1;
else return 0;
}
int main()
{
scanf("%d%d%d",&n,&a,&b);
for(int i=1;i<=n;i++)
{
scanf("%d",&h[i]);
maxn=max(maxn,(h[i]+a-1)/a);
}
//b-=a;
int l=1,r=maxn;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<l<<endl;
return 0;
}
例题2.雷达装置
首先将每个点对应可以放置雷达的区间映射到 x 轴上。按照区间右端点排序,贪心把雷达放在右端点。
code
#include<bits/stdc++.h>
using namespace std;
const int N=1005;
typedef double db;
int n,d;
struct node{
db x,y;
}a[N],b[N];
bool cmp(node x,node y){
return x.y<y.y;
}
int main()
{
scanf("%d%d",&n,&d);
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&a[i].x,&a[i].y);
if(fabs(a[i].y)>d) {cout<<"-1"<<endl;return 0;}
db l=sqrt(1.0*(d*d-a[i].y*a[i].y));
b[i].x=a[i].x-l;b[i].y=a[i].x+l;
}
sort(b+1,b+n+1,cmp);
db last=0;int ans=0;
for(int i=1;i<=n;i++)
{
if(i==1) {last=b[i].y;ans++;}
else
{
if(last<b[i].x) {last=b[i].y;ans++;}
}
}
cout<<ans<<endl;
return 0;
}
例题3.畜栏预定
贪心,每个牛能放就放,不能就新开一个畜栏来放。
用优先队列维护当前畜栏中牛离开时间的最小值。时间复杂度 \(O(n\log n)\)。
code
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
const int N=5e4+5;
int n,ans[N],cnt;
struct node{
int l,r,id;
}a[N];
bool cmp(node x,node y){
if(x.l==y.l) return x.r<y.r;
else return x.l<y.l;
}
priority_queue<pii,vector<pii>,greater<pii> > q;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].l,&a[i].r);
a[i].id=i;
}
sort(a+1,a+n+1,cmp);
cnt++;q.push(pii(a[1].r,1));
ans[a[1].id]=1;
for(int i=2;i<=n;i++)
{
if(q.empty())
{
ans[a[i].id]=1;
q.push(pii(a[i].r,1));
continue;
}
if(q.top().fi<a[i].l)
{
ans[a[i].id]=q.top().se;
int qwq=q.top().se;
q.pop();q.push(pii(a[i].r,qwq));
}
else
{
cnt++;ans[a[i].id]=cnt;
q.push(pii(a[i].r,cnt));
}
}
cout<<cnt<<endl;
for(int i=1;i<=n;i++) cout<<ans[i]<<endl;
return 0;
}
例题4.荷马史诗
哈夫曼树板子,虽然这么说,但是本喵以前没学过这东西啊qwq
直接贴个板子代码吧qwq
code
#include<bits/stdc++.h>
#define int long long
#define pii pair<long long,long long>
#define fi first
#define se second
using namespace std;
const int N=1e5+5;
int n,k,w[N],ans;
priority_queue<pii,vector<pii>,greater<pii> > q;
signed main()
{
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld",&w[i]);
q.push(pii(w[i],0));
}
while((q.size()-1)%(k-1)) q.push(pii(0,0));
while(q.size()>1)
{
int sum=0,maxn=0;
for(int i=1;i<=k;i++)
{
if(q.empty()) break;
sum+=q.top().fi;
maxn=max(maxn,q.top().se);
q.pop();
}
ans+=sum;
q.push(pii(sum,maxn+1));
}
cout<<ans<<"\n"<<q.top().se<<endl;
return 0;
}
1.排队接水
根据样例让时间短的人排在前面可以使排队总时间最短。把人按接水时间排序,统计答案。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1005;
int n,ans,sum;
struct node{
int t,id;
}a[N];
bool cmp(node x,node y){
return x.t<y.t;
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i].t),a[i].id=i;
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++) cout<<a[i].id<<" ",ans+=sum,sum+=a[i].t;
printf("\n%.2lf\n",1.0*(double)ans/n);
return 0;
}
2.砍树问题
先将树按横坐标排序,则由题意得 \(a_i\le \min(|p_i-p_{i-1}|,|p_{i+1}-p_i|)\)。
据此计算答案,注意判断树可能被全部砍完的情况。
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int inf=2e9;
int n,ans;
struct node{
int p,h;
}a[N];
bool cmp(node x,node y){
return x.p<y.p;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d",&a[i].p,&a[i].h);
sort(a+1,a+n+1,cmp);
a[0].p=-inf;a[n+1].p=inf;
for(int i=1;i<=n;i++)
{
int l=a[i].p-a[i-1].p,r=a[i+1].p-a[i].p;
int minn=min(l,r);
ans+=a[i].h-min(minn,a[i].h);
}
cout<<ans<<endl;
return 0;
}
3.出栈序列
两种情况:
- 当前栈顶元素大于未入栈元素最大值,将栈顶元素弹出;
- 反之,将未入栈元素最大值及它以前的元素全部入栈。
可用 \(f\) 数组维护 \([a_i,a_n]\) 的最大值。
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,a[N],maxn[N],tot;
stack<int> q;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
maxn[n]=a[n];
for(int i=n-1;i;i--) maxn[i]=max(a[i],maxn[i+1]);
int qwq=1;
while(a[qwq]!=n) q.push(a[qwq]),qwq++;
q.push(n);qwq++;
while(qwq<=n)
{
tot++;if(tot>10) break;
//cout<<qwq<<endl;
while(!q.empty()&&q.top()>maxn[qwq]) cout<<q.top()<<" ",q.pop();
for(int i=qwq;i<=n;i++)
{
//cout<<"qwq"<<i<<endl;
q.push(a[i]);
if(a[i]==maxn[qwq]) {qwq=i+1;break;}
}
}
while(!q.empty()) cout<<q.top()<<" ",q.pop();
return 0;
}
4.序列问题
我们知道,将当前答案再或一个数,结果一定不会变小;将当前答案再与一个数,结果一定不会变大。
所以第一个答案就是所有数或起来,第二个答案连续子序列的长度为 \(k\)。
考虑把每个数二进制拆分,求前缀和来快速算出区间内所有数与起来的答案。
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,k,a[N],ans1,maxn;
int sum[N][35];
bool b[N][35];
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),ans1|=a[i];
cout<<ans1<<" ";
for(int i=1;i<=n;i++)
{
for(int j=0;j<=30;j++) b[i][j]=(a[i]>>j)&1,sum[i][j]=sum[i-1][j]+b[i][j];
}
for(int l=1;l<=n-k+1;l++)
{
int r=l+k-1,ans=0;
for(int j=0;j<=30;j++)
{
int cnt=((sum[r][j]-sum[l-1][j])==k);
//if(j==0) cout<<l<<" "<<r<<" "<<j<<" "<<sum[l-1][j]<<" "<<sum[r][j]<<endl;
ans+=(1<<j)*cnt;
}
//if(ans>0) cout<<l<<" "<<r<<" "<<ans<<endl;
maxn=max(maxn,ans);
}
cout<<maxn<<endl;
return 0;
}
5.仙人之环
最优方案是优先选不在环上的边,如果不够就断开最大的环继续选。
dfs 判断环的大小及非环边的数量,然后将环的大小从大到小排序,贪心取边。
代码实现相当恶心,比如笔者忘记了调用 cmp 函数以致调了 1.5h,各位引以为戒(
code
#include<bits/stdc++.h>
using namespace std;
const int N=4e6+5;
int n,m,k;
struct node{
int nxt,to;
}e[N];
int head[N],cnt;
void add(int u,int v){
e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;
}
bool vis[N];
int tot,h[N],num,ans,dfn[N];
void dfs(int u,int last,int dep)
{
vis[u]=1;dfn[u]=dep;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(vis[v]&&v!=last&&dfn[u]>dfn[v]) h[++num]=dfn[u]-dfn[v]+1;
else if(!vis[v]) dfs(v,u,dep+1);
}
}
bool cmp(int x,int y){
return x>y;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1,u,v;i<=m;i++)
{
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
for(int i=1;i<=n;i++) if(!vis[i]) ans++,dfs(i,0,0);
//dfs(1,0);
sort(h+1,h+num+1,cmp);
int ss=0;
for(int i=1;i<=num;i++) ss+=h[i];
int w=m-ss,t=1;
while(k)
{
if(!w)
{
w+=h[t]-1;
t++;k--;
}
else if(k>w) {k-=w,ans+=w,w=0;continue;}
else {ans+=k;break;}
}
cout<<ans<<endl;
return 0;
}
本文来自博客园,作者:樱雪喵,转载请注明原文链接:https://www.cnblogs.com/ying-xue/p/16582150.html