2 条题解

  • 2
    @ 2017-10-06 19:08:41

    这道题简化之后难度其实并不大。
    首先第一个感觉就是贪心。肯定是每个数尽量保留最高位的那一个。但如果某一位有偶数个数保留,就会变回0,所以反而就不是最优秀。这是有个骗分技巧:关于最后的四个点,我们发现数据是成百上千的,而位数只有31位,几乎一定可以覆盖到,于是用贪心填高位,如果已经被填,就更改第一位(这是奇数,如果是原题不限奇偶就尴尬了)
    但还是考虑一下正解,毕竟大数据可以出特殊数据来卡,只是我不会造而已。
    这时就把本题目转换为一个费用流就可以了。
    但是不会的同学可以用下列算法代替,将本题化最大二分图权匹配。可以上网搜索KM算法(也可以看下面)。
    ———————————————————————————————————————————————————————————————————
    将本题剥开,是一道图论题。很容易想到,因为答案属于int型,所以只对应有31位的二进制。将每个数与它二进制中存在1的位相连,而又要尽量的一一匹配,所以是一个二分图带权匹配。总体思路就是能娶女神就娶女神,女神有男朋友了就抢,女神的前男友悲惨的找另一个最优选择,找不到又抢回来,这个男生就只有悲惨的找次优选择,或者让女神的男友找次优选择。总体思路:贪心+抢!
    我们用男孩子找女孩来说明。(十八岁以下请绕行)

    #include<bits/stdc++.h>
    const int maxn=1e6;
    inline const void read(int &a)
    {
        a=0;
        char c=getchar();
        while(c<'0'||c>'9')c=getchar();
        while(c>='0'&&c<='9')
        {
            a=(a<<1)+(a<<3)+c-'0';
            c=getchar();
        }
    }
    inline const void write(int a)
    {
        if(a>9)write(a/10);
        putchar(a%10+'0');
    }
    inline const int max(int a,int b)
    {
        if(a>b)return a;
        else return b;
    }
    inline const int min(int a,int b)
    {
        if(a<b)return a;
        else return b;
    }
    int n,d=0;
    int point[maxn],next[maxn],to[maxn],len[maxn];//len指这条边的权值,实际上就是指向的那一位的大小,即时这对男女的互相倾心值。
    int exboy[maxn],exgirl[maxn],boy[maxn],girl[maxn],three[maxn];//exboy和exgirl表示男孩女孩对暗恋者的期望值。boy和girl分别指这个男女生现任女男友,three是女生离做小三的距离,也就是要被某个男生作为首选,最少还差多少与他的倾心值。
    bool visboy[maxn],visgirl[maxn];//记录是否已经参与本次违背伦理的换男女友活动
    const bool found(int p)//判断是否能够为这个男生牵线,并返回是否成功
    {
        visboy[p]=true;//这个男生卷进来男生
        int side=point[p];
        while(side)//遍历他喜欢的每一个女生
        {
            if(!visgirl[to[side]])//每个女生只被抢一次,否则会死循环(多么可怕啊!)
            {
                if(exboy[p]+exgirl[to[side]]==len[side])//假设这个女孩能作为首选
                {   
                    visgirl[to[side]]=true;//纯洁的女孩也被卷了进来
                    if(!girl[to[side]]||found(girl[to[side]]))//如果女孩没有男友或者他的男友能另找女孩,就在一起!
                    {
                        girl[to[side]]=p;//女生的男朋友
                        boy[p]=to[side];//男生的女朋友
                        return true;//共同甜蜜的宣布牵手成功
                    }
                }
                else//这个女孩不是首选
                        three[to[side]]=min(three[to[side]],exboy[p]+exgirl[to[side]]-len[side]);//这个女孩暗下决心,还要多少倾心值才能成为首选(小三)
            }
            side=next[side];
        }
        return false;
    }
    void solve()
    {
        for(int i=1;i<=n;i++)//试着给每个男孩子找女朋友(找啊找啊找朋友,找到一个女朋友……)
        {
            memset(three,0x7f,sizeof(three));
            while(true)
            {   
                memset(visgirl,false,sizeof(visgirl));//女孩没有被抢过
                memset(visboy,false,sizeof(visboy));//男孩没有被想过
                if(found(i))break;
                int minus=three[0];//离做小三还有多少距离
                for(int j=1;j<=32;j++)if(!visgirl[j])minus=min(three[j],minus);//保证让最容易做小三的那一个做小三。
                if(minus==three[0])break;//如果永远做不了小三(three[0]==INF),就做一辈子的单身狗……
                for(int j=1;j<=n;j++)if(visboy[j])exboy[j]-=minus;//所有男孩子因为有情敌而降低期望
                for(int j=1;j<=32;j++)
                    if(visgirl[j])exgirl[j]+=minus;//女孩们因为又男孩子抢而得意忘形,增加期望。(这个地方可以发现,因为是男女期望之和,所以除了做小三的那个女孩子,其它的配对均不受影响)
            }
        }
    }
    inline const void add(int a,int b,int c)
    {
        next[++d]=point[a];//大家互相牵挂,互做备胎
        to[d]=b;
        point[a]=d;
        len[d]=c;
        exboy[a]=max(exboy[a],len[d]);//最初,男孩子都很天真,以为最爱的女神一定属于自己!(马上就是命运的打击了……)
    }
    int main()
    {
        memset(exboy,0,sizeof(exboy));
        memset(exgirl,0,sizeof(exgirl));
        memset(girl,0,sizeof(girl));
        memset(boy,0,sizeof(boy));
        read(n);
        int k;
        for(int i=1;i<=n;i++)
        {
            read(k);
            for(int j=1;j<=32;j++)//三十二位美丽的女嘉宾登场!!!这里其实可以保证更加美丽的女嘉宾后加边。根据链式前向星的特点,后加入的美女一定越靠近每个点的第一条边,更先被考虑。
                if((1<<j-1)&k)add(i,j,1<<j-1);//互相倾慕的对象,在其脉脉传情之时牵带权线
        }
        solve();
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            if(boy[i])ans+=1<<boy[i]-1;//情侣们炫耀这爱情的成果!!!
            else ans^=1;//关于狗们:这里是一个贪心,如果这个男孩子是个单身狗,他就只能骚扰最丑的那个女生,我们不让他影响各位女神们。这个悲惨的男孩子也只能做些这种事情了……(如LYC)
        }
        write(ans);
        return 0;
    }
    

    如果你觉得这篇题解太污,请不要大声喧哗,拍照,以免影响他人。

  • 1
    @ 2017-11-07 21:28:10

    贪心一波:因为这道题有个特殊的限制,即边权等于女生的点权。所以直接用一种很神奇的贪心方法,从高到低查找所有女生,每一次寻找所连男生中潜力最大者相连(即接下来还有较大的连边)。但是在真正的带权匹配中,这个算法是错误的。参见另一篇题解。

    #include<bits/stdc++.h>
    using namespace std;
    int n,a[1001],v[32][1001],h[1001],ans2[32],b[1001][31];
    long long ans=0;
    struct node
    {
        int a,data;
    };
    inline const void read(int &a)
    {
        a=0;
        char c=getchar();
        while(c<'0'||c>'9') c=getchar();
        while(c>='0'&&c<='9')
        {
            a=(a<<1)+(a<<3)+c-'0';
            c=getchar();
        }
    }
    bool com1(node aa,node bb)
    {
        return aa.data<bb.data;
    }
    void find(node *x,int len)
    {
        node temp[1001];
        int jk=0;
        sort(x+1,x+1+len,com1);
        if(x[1].data==0)
        {
            h[x[1].a]=1;
            return;
        }
        temp[++jk].a=x[1].a;
        for(int i=1;i<=b[x[1].a][0];i++)
        {
            if(b[x[1].a][i]<x[1].data)
            {
                temp[jk].data=b[x[1].a][i];
                break;
            }
        }
        for(int i=2;i<=len;i++)
        {
            if(x[i].data==x[i-1].data)
            {
                temp[++jk].a=x[i].a;
                for(int o=1;o<=b[x[i].a][0];o++)
                {
                    if(b[x[i].a][o]<x[i].data)
                    {
                        temp[jk].data=b[x[i].a][o];
                        break;
                    }
                }
            }
            else break;
        }
        if(jk==1)
        {
            h[temp[jk].a]=1;
            return;
        }
        else 
        {
            find(temp,jk);
        }
    }
    bool com(int a,int b)
    {
        return a>b;
    }
    int main()
    {
        //freopen("ji.in.txt","r",stdin);
        int i,k,s;
        memset(v,0,sizeof(v));
        memset(h,0,sizeof(h));
        memset(ans2,0,sizeof(ans2));
        read(n);
        for(i=1;i<=n;i++) read(a[i]);
        for(i=1;i<=n;i++)
        {
            for(k=31;k>=0;k--)
            {
                if(((unsigned int)1<<k)<=a[i]&&(a[i]&((unsigned int)1<<k)))
                {
                    b[i][++b[i][0]]=k;
                    v[k][++v[k][0]]=i;
                }
            }
        }
        for(i=31;i>=0;i--)
        {
            if(v[i][0])
            {
                if(v[i][0]==1&&!h[v[i][1]])
                {
                    ans2[i]=1;
                    h[v[i][1]]=1;
                }
                else 
                {
                    node temp[1001];
                    int jk=0;
                    for(int j=1;j<=v[i][0];j++)
                    {
                        if(!h[v[i][j]])
                        {
                            temp[++jk].a=v[i][j];
                            int mm=0;
                            for(int o=1;o<=b[v[i][j]][0];o++)
                            {
                                if(!ans2[b[v[i][j]][o]]&&b[v[i][j]][o]<=i)
                                {
                                    mm++;
                                    if(mm==2)
                                    {
                                        temp[jk].data=b[v[i][j]][o];
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    if(jk)
                    {
                        ans2[i]=1;
                        find(temp,jk);
                    }
                }   
            }
        }
        for(i=1;i<=n;i++)
        {
            if(!h[i])
            ans2[0]=abs(ans2[0]-1);
        }
        for(i=0;i<=31;i++)
            if(ans2[i])
                ans+=((unsigned int)1<<i);
        cout<<ans<<endl;
        return 0;
    }
    
  • 1

信息

难度
9
分类
(无)
标签
(无)
递交数
14
已通过
3
通过率
21%
上传者