0x07 贪心

被虐爆了。。。贪心这种玄学东西还可以证吗??除了范围缩放算是可以想想比较经典(倍增第一题?)。。。

poj3614:这道题想了很久,并没有想到是把minSPF按大到小排序,一直的思想是小的就小到大排序。后来仔细分析下,这题做法是n^2的,假如排序是要控制一个值的单调性,再用一个for来贪心,假如是小到大排序,选取时应该取大的还是应该取小的?直观上想选小的,因为后面的下界大,但是,假如当前位置区间很大而后一个很小就出问题了。假如是大到小排序,相当于逆过来,此时尽量取大,是基于当前的上界的,对于下界已经没有必要关注了,因为是单调递减的。考虑到这题没有权值,也可以看作全为1,当前的最大和后面的匹配无法得到更优解。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;

struct node
{
    int x,y;
}a[3100],b[3100];
bool cmp(node n1,node n2){return n1.x>n2.x;}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y);
    for(int i=1;i<=m;i++)scanf("%d%d",&b[i].x,&b[i].y);
    sort(a+1,a+n+1,cmp);
    sort(b+1,b+m+1,cmp);
    
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
            if(a[i].x<=b[j].x&&b[j].x<=a[i].y&&b[j].y>0)
            {
                b[j].y--;ans++;
                break;
            }
    }
    printf("%d\n",ans);
    return 0;
}
poj3614

poj3190:这题倒是没什么。。。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;

struct node
{
    int x,y,z;
    friend bool operator>(node q1,node q2){return q1.y>q2.y;}
}a[51000];priority_queue< int,vector<node>,greater<node> >q;
bool cmp(node n1,node n2){return n1.x<n2.x;}

int bel[51000];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&a[i].x,&a[i].y), a[i].z=i;
    sort(a+1,a+n+1,cmp);
    
    int ans=1;q.push(a[1]);bel[a[1].z]=1;
    for(int i=2;i<=n;i++)
    {
        node t=q.top();
        if(t.y<a[i].x)q.pop(),bel[a[i].z]=bel[t.z];
        else ans++,bel[a[i].z]=ans;
        q.push(a[i]);
    }
    printf("%d\n",ans);
    for(int i=1;i<=n;i++)printf("%d\n",bel[i]);
    return 0;
}
poj3190

poj1328:也算是小小的套路题吧。在越后放越好,“决策包容性”虽然运用的不是挺灵活,但是还蛮好想(所以这题难度在转换问题变成在n个区间里都要有至少一个点??)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;

struct node
{
    double l,r;
}a[1100];
bool cmp(node n1,node n2)
{
    if(fabs(n1.l-n2.l)<1e-8)return n1.r<n2.r;
    return n1.l<n2.l;
}

bool v[1100];
int main()
{
    int n,T_T=0;double R;
    while(scanf("%d%lf",&n,&R)!=EOF)
    {
        if(n==0&&R==0)break;
        T_T++;
        
        double x,y;bool bk=true;
        for(int i=1;i<=n;i++)
        {
            scanf("%lf%lf",&x,&y);
            if(y>R)bk=false;
            double dis=(sqrt((R*R)-(y*y)));
            a[i].l=x-dis;a[i].r=x+dis;
        }
        if(bk==false){printf("Case %d: -1\n",T_T);continue;}
        sort(a+1,a+n+1,cmp);
        
        int ans=0;
        memset(v,false,sizeof(v));
        for(int i=1;i<=n;i++)
        {
            if(v[i]==false)
            {
                ans++;v[i]=true;double pos=a[i].r;
                for(int j=i+1;j<=n;j++)
                    if(a[j].l<=pos)v[j]=true,pos=min(pos,a[j].r);
                    else break;
            }
        }
        printf("Case %d: %d\n",T_T,ans);
    }
    return 0;
}
poj1328

upd:我之前写的实在是太不优秀了。

把区间弄出来以后,相当于用最小的点覆盖所有区间,那么对于区间相互包含,不用管,对于区间分开没有交集,直接新开

那么就要处理覆盖一部分的情况。按L排序,决策包容,能选就选,L区间单调增,维护个R单调减,非法就新开

 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;

struct node{double L,R;}a[1100];
bool cmp(node n1,node n2){return n1.L<n2.L;}
int main()
{
    int n,T_T=0; double m,x,y;
    while(scanf("%d%lf",&n,&m)!=EOF)
    {
        if(n==0&&m==0)break;
        
        bool bk=true;
        for(int i=1;i<=n;i++)
        {
            scanf("%lf%lf",&x,&y);
            if(y>m)bk=false;
            a[i].L=x-sqrt(m*m-y*y);
            a[i].R=x+sqrt(m*m-y*y);
        }
        if(bk==false){printf("Case %d: -1\n",++T_T);continue;}
        sort(a+1,a+n+1,cmp);
        
        int ans=1;double R=a[1].R;
        for(int i=1;i<=n;i++)
        {
            if(a[i].L>R)
                ans++, R=a[i].R;
            else
                R=min(R,a[i].R);
        }
        printf("Case %d: %d\n",++T_T,ans);
    }
    return 0;
}
poj1328(upd)

 

 

 

NOIP2012国王游戏:完全没有见过这种题型。。书上说微扰这个东西经常用于以排序为贪心策略的问题?反正这种数学东西证明,假如想到的话一般的还是不难推的。

顺便安利一道题bzoj3174: [Tjoi2013]拯救小矮人

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;

int n,m;LL k;
LL a[510000],b[510000],tt[510000];
void mergesort(int l,int r)
{
    if(l==r)return ;
    int mid=(l+r)/2;
    mergesort(l,mid);mergesort(mid+1,r);
    
    int i=l,j=mid+1,p=l;
    while(i<=mid&&j<=r)
    {
        if(b[i]<=b[j])tt[p++]=b[i++];
        else              tt[p++]=b[j++];
    }
    while(i<=mid)tt[p++]=b[i++];
    while(j<=r)  tt[p++]=b[j++];
    
    for(int i=l;i<=r;i++)b[i]=tt[i];
}
int clen;LL c[510000];
bool check(int l,int r)
{
    if(r>n)return false;
    
    int blen=r-l+1;
    for(int i=l;i<=r;i++)b[i-l+1]=a[i];
    mergesort(1,blen);
    
    int i=1,j=1,p=1;
    while(i<=blen&&j<=clen)
    {
        if(b[i]<=c[j])tt[p++]=b[i++];
        else              tt[p++]=c[j++];
    }
    while(i<=blen)tt[p++]=b[i++];
    while(j<=clen)tt[p++]=c[j++];
    
    p--;
    LL sum=0;
    for(int i=1;i<=m;i++)
    {
        if(p-i+1<=i)break;
        sum+=(tt[p-i+1]-tt[i])*(tt[p-i+1]-tt[i]);
    }
    
    if(sum<=k)
    {
        clen=p;
        for(int i=1;i<=clen;i++)c[i]=tt[i];
        return true;
    }
    else return false;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%lld",&n,&m,&k);
        for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
        
        int ed,ans=0;
        for(int st=1;st<=n;st=ed+1)
        {
            ed=st;int L=1;
            clen=0;c[++clen]=a[st];
            while(L>0)
            {
                if(check(ed+1,ed+L)==true)
                {
                    ed=ed+L;
                    L*=2;
                }
                else L/=2;
            }
            ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}
NOIP2012国王游戏

poj2054:这题是真的难啊,大开眼界。。。有一个性质,树中除根以外的最大点,一定会在它的父亲被染色之后被染色,那么居然依此来合并点。此时我们的目标是先把染色顺序给弄好,选择了放弃原本的权值转而自己定义新的权值!定义为合并包含的所有点的权值平均值。是可以数学归纳法证明的(这个东西不太会啊,我只用过来证柯西不等式。。。)具体做法我就是用链表存储顺序,然后用一个并查集维护合并了的块。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;

int F[1100];
int findfa(int x)
{
    if(F[x]==x)return x;
    F[x]=findfa(F[x]);return F[x];
}
int u[1100],fa[1100];
struct node{int d,z;}c[1100];
bool v[1100];
int bef[1100],aft[1100],la[1100];
int main()
{
    int n,R;
    while(scanf("%d%d",&n,&R)!=EOF)
    {
        if(n==0&&R==0)break;
        
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&c[i].d);u[i]=c[i].d;
            c[i].z=1;F[i]=i;
            bef[i]=aft[i]=-1;la[i]=i;
        }
        int x,y;
        for(int i=1;i<n;i++)
            scanf("%d%d",&x,&y), fa[y]=x;
            
        memset(v,false,sizeof(v));v[R]=true;
        for(int i=1;i<n;i++)
        {
            int id=-1;
            for(int j=1;j<=n;j++)
                if(v[j]==false)
                    if(id==-1||c[id].d*c[j].z<c[j].d*c[id].z)id=j;
                    
            v[id]=true;
            int truefa=findfa(fa[id]);F[id]=truefa;
            c[truefa].d+=c[id].d;
            c[truefa].z+=c[id].z;
                        
            bef[id]=la[truefa];
            aft[la[truefa]]=id;
            la[truefa]=la[id];
        }
        
        LL ans=0;
        for(int i=1;i<=n;i++)
            ans+=(LL(i))*(LL(u[R])), R=aft[R];
        printf("%lld\n",ans);
    }
    return 0;
}
poj2054

 

posted @ 2018-06-28 20:11  AKCqhzdy  阅读(175)  评论(0编辑  收藏  举报