[NOIp复习计划]:贪心

1 篇文章 0 订阅

[bzoj 4029][HEOI2015]定价
随便乱搞一下就好了

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int read()
{
    char ch;int s=0,f=1; ch=getchar();
    while(ch>'9'||ch<'0') { if(ch=='-') f*=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { s=s*10+ch-48;ch=getchar(); }
    return s*f;
}
int t,n,m;
int add(int x)
{
    int k=1;
    while(x%10==0) k*=10,x/=10;
    return k;
}
int getans(int x)
{
    while(x%10==0) x/=10;
    int t=x%10,a=0;
    while(x) x/=10,a++;
    if(t==5) return 2*a-1;
    else return 2*a;
 }
 void work()
 {
    int ans=n,zz=getans(n);
     while(1)
     { 
         n+=add(n);
         if(n>m) break;
         int tt=getans(n);
         if(tt<zz) zz=tt,ans=n;


     }
     printf("%d\n",ans);
 }
int main()
{
    t=read();
    while(t--)
    {
            n=read(),m=read();
            work();
    }
     return 0;
}


[bzoj 4027][HEOI2015]兔子与樱花
对于每一个节点排个序,能删就删,更新答案

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=2000010;
int n,m,ans,cnt;
int to[maxn],next[maxn],head[maxn],v[maxn],f[maxn],p[maxn];
int rd(){
    int ret=0,f=1;  char gc=getchar();
    while(gc<'0'||gc>'9') {if(gc=='-')f=-f;   gc=getchar();}
    while(gc>='0'&&gc<='9')   ret=ret*10+gc-'0',gc=getchar();
    return ret*f;
}
void add(int a,int b){
    to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
bool cmp(int a,int b){
    return f[a]<f[b];
}
void dfs(int x){
    int i;
    for(i=head[x];i!=-1;i=next[i])dfs(to[i]);
    for(p[0]=0,i=head[x];i!=-1;i=next[i])p[++p[0]]=to[i];
    sort(p+1,p+p[0]+1,cmp);
    for(i=1;i<=p[0];i++) if(f[x]+f[p[i]]-1<=m)f[x]+=f[p[i]]-1,ans++;
}
int main(){
    n=rd(),m=rd();
    int i,j,a,b;
    memset(head,-1,sizeof(head));
    for(i=1;i<=n;i++)    v[i]=rd();
    for(i=1;i<=n;i++){
        a=rd(),f[i]=a+v[i];
        for(j=1;j<=a;j++){
            b=rd()+1;
            add(i,b);
        }
    }
    dfs(1);
    printf("%d",ans);
    return 0;
}

[bzoj 3624][Apio2008]免费道路
求一颗满足条件的生成树,我们可以选择先把石子路全部加进生成树里,在从普通道路里选编,逐步替换十字路,但这样复杂度太高,我们可以考虑反过来,先对普通道路做生成树,如果生成出的不是树,是森林,就去找石子路,这是找到的石子路是要求生成树中必含有的,不然就不满足是一棵树,然后倒过来,把必要的石子路加进生成树,如果小于k,就乱加,然后随便加普通道路,一定是棵树。

#include<cstdio>
#include<iostream>
using namespace std;
const int maxn = 20005;
const int maxm = 200005;
struct _s_{
    int f,t,pid;bool tag;
    _s_(int a,int b,int c,bool d):f(a),t(b),pid(c),tag(d){}
    _s_(){}
}path1[maxm],path2[maxm];
int fa[maxn];
long long tot1,tot2;
int n,m,K;
void reset(){
    for(int i=1;i<=maxn;i++)
        fa[i] = i;
}
int getroot(int x){
    return fa[x]==x?x:fa[x]=getroot(fa[x]);
}

int main(){
    scanf("%d%d%d",&n,&m,&K);
    for(int i=1;i<=m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        if(c==1)path1[++tot1]=_s_(a,b,c,0);
        else path2[++tot2]=_s_(a,b,c,0);
    }
    if(tot2<K){puts("no solution");return 0;}
    reset();
    int k=0;
    for(int i=1;i<=tot1;i++){
        _s_ _now_ = path1[i];
        int ff=getroot(_now_.f);
        int ft=getroot(_now_.t);
        if(ff!=ft){
            fa[ff]=ft;
            k++;
        }
    }
    if(n-1-k > K){puts("no solution");return 0;}
    for(int i=1;i<=tot2;i++){
        _s_ _now_ = path2[i];
        int ff=getroot(_now_.f);
        int ft=getroot(_now_.t);
        if(ff!=ft){
            fa[ff]=ft;
            k++;
            path2[i].tag=true;
        }   
        if(k == n-1)break;
    }
    if(n-1>k){puts("no solution");return 0;}
    k=0;
    reset();
    for(int i=1;i<=tot2;i++){
        if(path2[i].tag){
            _s_ _now_=path2[i];
            int ff=getroot(_now_.f);
            int ft=getroot(_now_.t);
            if(ff!=ft){
                fa[ff]=ft;
                k++;
                path2[i].tag=true;
            }   
        }
        if(k == K)break;
    }
    if(k<K){
        for(int i=1;i<=tot2;i++){
            if(path2[i].tag==0){
                _s_ _now_=path2[i];
                int ff=getroot(_now_.f);
                int ft=getroot(_now_.t);
                if(ff!=ft){
                    fa[ff]=ft;
                    k++;
                    path2[i].tag=true;
                }   
            }
            if(k == K)break;
        }
    }
    if(k!=K){puts("no solution");return 0;} 
    if(k<n-1){
        for(int i=1;i<=tot1;i++){
            _s_ _now_ = path1[i];
            int ff=getroot(_now_.f);
            int ft=getroot(_now_.t);
            if(ff!=ft){
                fa[ff]=ft;
                k++;
                path1[i].tag=true;
            }
            if(k==n-1)break;                
        }   
    }
    if(k < n-1){puts("no solution");return 0;} 
    for(int i=1;i<=tot2;i++)if(path2[i].tag)printf("%d %d %d\n",path2[i].f,path2[i].t,path2[i].pid);
    for(int i=1;i<=tot1;i++)if(path1[i].tag)printf("%d %d %d\n",path1[i].f,path1[i].t,path1[i].pid);
    return 0;
}

[bzoj 3728][PA2014]Final Zarowki
对p排序,然后把w插入set中,对于每个p,在set中找大于它的最小值i,再把i插入到一个堆里,最后乱搞更新答案,挺经典的。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 700005;
int p[MAXN],w[MAXN],n,k;long long ans;
multiset<int> tr;
priority_queue<int> pq;
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)scanf("%d",p+i);
    for(int i=1;i<=n;i++)scanf("%d",w+i),tr.insert(w[i]);
    sort(p+1,p+n+1);
    for(int i=1;i<=n;i++){
        multiset<int>::iterator it=tr.upper_bound(p[i]);
        if(it!=tr.begin()){
            int t=*(--it);
            tr.erase(it);
            ans+=p[i];
            pq.push(p[i]-t);
        }
    }
    if(tr.size()>k){puts("NIE");return 0;}
    for(multiset<int>::iterator i=tr.begin();i!=tr.end();i++){
        k--;ans+=*i;
    }
    while(k--){ans-=pq.top();pq.pop();}
    printf("%lld",ans);
    return 0;
}

[bzoj 3850]ZCC Loves Codefires
终于做到水题了,终于是水题了。
这里写图片描述
跟某个noip的题很像,忘了名字了……

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
typedef long long ll;
struct _s_{
    ll e,k;
    bool operator < (_s_ b)const{
        return this->k*this->e+b.k*(b.e+this->e) < this->k*(this->e+b.e)+b.e*b.k;
    }
}data[MAXN];
ll n,ans,ti;
int main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&data[i].e);
    }
    for(int i=1;i<=n;i++)scanf("%lld",&data[i].k);
    sort(data+1,data+n+1);
    for(int i=1;i<=n;i++){
        ti += data[i].e;
        ans += ti*data[i].k;
    }
    printf("%lld",ans);
    return 0;
} 

[bzoj 1150][CTSC2007]数据备份Backup
写这个题的时候我是崩溃的,如果这个可以称作是贪心的话,那我做的每一个网络流的题都是贪心了。
一个显然的条件,两个点之间的距离我们看作是一条边,那这题是让我们挑k个没有交点(端点也算)的边,并使边值最小。
下面开始鬼畜了……
我们一开始可以把n条边放入堆里,贪心的选小的,怎么处理没有交点呢?我们一旦选取一个点,我们就把这条边,左边的那条边,右边的那条边在堆里删掉,再加入一条边,边值=原来左边的值+原来右边的值-原来那条边的值,这条边的位置代替了原来三条边的位置,这是为什么呢,我们可以把它看作是给程序一个反悔的机会(这不就是网络流的反向弧吗!!!),之后剩下的就是好多好多细节
定位边的位置用双向链表,若要化简操作可以左右各加一个边界点,左边界点的向左指针指向自己,右边边界点的向右节点指向自己
堆的删除可以另开一个堆,把要删除的放进新堆,一旦新旧堆堆顶是一样的就表示这个值已经被删除了。
于是终于能A这个题了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 100005;
ll inf = 0x3f3f3f3f3f3f3f3f;
typedef pair<ll,ll> P;
int n,k;
int l[MAXN<<1],r[MAXN<<1],tot;
ll val[MAXN],ans,pr;
priority_queue<P> pq[2];
void maintain(){
    while((pq[0].size()!=0) && (pq[1].size() !=0)&& (pq[0].top()==pq[1].top())){
        pq[0].pop();
        pq[1].pop();
    }
}
void push(P x){
    maintain();
    pq[0].push(x);
}
P top(){
    maintain();
    return pq[0].top();
}
void del(P x){
    maintain();
    pq[1].push(x);
}
void pop(){
    maintain();
    if(pq[0].size()){
        pq[0].pop();
    }
}
int main(){
    scanf("%d%d",&n,&k);
    l[1]=1;r[++tot]=2;val[tot]=inf;
    scanf("%lld",&pr);
    for(int i=1;i<n;i++){
        ll a,t;scanf("%lld",&a);t=a;a-=pr;pr=t;
        l[++tot]=tot-1;r[tot]=tot+1;
        val[tot]=a;
    }
    r[tot]=++tot;l[tot]=tot-1;r[tot]=tot;val[tot]=inf;
    for(int i=2;i<=tot-1;i++){
        push(make_pair(-val[i],i));
    }   
    for(int i=1;i<=k;i++){
        P now=top();
        pop();
        ans -= now.first;
        int t=now.second;
        int new_val=val[l[t]]+val[r[t]]-val[t];
        if(l[t]!=1)
            del(P(-val[l[t]],l[t]));
        if(r[t]!=tot)
            del(P(-val[r[t]],r[t]));
        if(l[t]==1){
            l[r[r[t]]]=1;
            r[l[t]]=r[r[t]];
        }else if(r[t]==tot){
            r[l[l[t]]]=tot;
            l[r[t]] = l[l[t]];
        }
        else{
            if(l[l[t]]!=0)
                r[l[l[t]]]=t;
            if(r[r[t]]!=0)
                l[r[r[t]]]=t;
            val[t]=new_val;
            l[t]=l[l[t]];
            r[t]=r[r[t]];
            push(P(-new_val,t));
        }
    }
    printf("%lld",ans);
}

to be continued …

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值