• [bzoj3371][poj2009][Usaco2004 Mar]Moo University


    标题这么长的。。真是让感觉人头大脚轻。 

    贴题面先。


     Description

        Moo大学的餐厅必须为$C(1leq Cleq 1000)$头入学的奶牛新生定制比萨饼.比萨饼可以在比萨小屋订做.一个饼只能给一头奶牛吃.尽管比萨小屋想为每一头奶牛都做一个比萨饼,但是他们必须服从以下三个条件:
    ·比萨小屋拥有$T(1leq Tleq 30)$种素菜馅,每个比萨饼有且仅有$K(1leq Kleq T)$种馅。
    ·任何一个比萨饼里不能有两种相同的馅(比如一个比萨饼里不能有两份洋葱)。
    ·任何两个比萨饼不能有完全相同的馅,即任何两个比萨饼至少有一种馅是不同的。当然,馅的编号是1到T。
        Moo大学的奶牛对比萨饼馅十分挑剔,有的奶牛可能什么馅都不喜欢。一头奶牛只吃那种所有的馅他都喜欢的比萨饼。
        请你计算最多能喂几头奶牛。

    Input

        第1行:三个整数C,T,K.
        第2到C+1行:每行有若干个用空格分开的整数,表示这头奶牛喜欢的馅.第一个数是这头奶牛喜欢的馅的种数.接下来的几个整数是这头奶牛喜欢的馅的编号。

    Output

    一个整数,即最多能有几头牛被喂养。

    Sample Input

    3 2 1
    2 2 1
    1 1
    1 2

    Sample Output

    2
    样例说明
    只能生产两种饼:一个有1号馅的比萨饼和一个有2号馅的比萨饼。第一个饼给第1号牛,第二块饼给3号牛,这样两头牛被喂饱了.同时将三头都喂饱的办法不存在。

    题面并不是很容易看懂,理解应该是没问题的。重要的几句话是:

    1:不能有两头牛的pizza组成完全相同。

    2:一个pizza上必须有且仅有k种馅料。

    3:pizza上的馅料不能重复。

    以上就是说每头牛的喜好与pizza的构成可以用一个01串或者bitset来表示,且是1的位有且只有k个,pizza之间互不相同。

    4;一头牛能与一个pizza匹配当且仅当牛的01串中为0的位,pizza的01串也是0。


    Claris说是暴搜啊?!仔细看看确实是。但大概还是基于二分图匹配的暴搜。

    目前正在研究,如果想不想看我口胡的非正解非满分做法,请右上角关闭页面然后等我完善再来看。


     口胡部分:

    这道题我并没有A,拿到了80pts。做法基于二分图匹配但还包含贪心。很玄乎,给大家提供思路而已。

    看起来就是个二分图匹配,但是数据范围?有30种馅料,最多可以存在$inom{15}{30}$大约$1e12$种pizza。但最终能匹配的能有几个?显然不会超过n。如果枚举所有的点,连上所有的边,额,呵呵。

    所以显然不能按上面说的那样来做。需要在寻找增广路的时候,动态寻找。

    具体实现:

    首先能跟给定奶牛匹配的pizza,我们说过,必须在奶牛为0的位上也是0。但我们要凑够k种馅料,所以还需要在保证上述要求的情况下,填上k个1。我们假设当前奶牛喜欢t种馅,即不喜欢m-t种馅,即有m-t位为0。显然t<k的牛什么也给不了,挑食太多就没法满足。其他的牛呢?我们这k位怎么填?我的做法是按照枚举的顺序填就行,先向靠前的位填1,直到填完。然后我们得到一个01串,可以将其转成int之后用map来存储这个pizza是否被其他牛占领过了。

    下面是出现问题的地方。

    如果已经被占领过,比较当前牛和之前占领的牛哪一个喜欢的馅料多。由喜欢馅料多的牛做出让步,把这个pizza让给少的,因为它更矫情,更难满足。而且如果两头牛能一起抢一个pizza,pizza一定是两头牛的喜好的交集。这个贪心我并不会证明是正确的,而且最尴尬的在于两头牛喜欢的馅料数相等,还不完全相同,比如(1,3,4)和(3,4,5)抢(3,4),并不好说给谁。我的做法里默认先来后到,所以会有错误。

    略过这里不提,按如上方法增广,如果不能增广就表示这个牛匹配不到pizza。每头牛匹配完,即可得出答案。

    Tips:其实还有很难解决的一个地方,就是如何按顺序枚举在(m-t)个空位上填入k个1。我发现了一位博主的做法用了上去。很强。


    因为还没有做出正解,所以希望有dalao可以切掉这道题并告诉本蒟蒻怎么解决错误的贪心或者正解也好!

    附80pts

    #include<iostream>
    #include<cstdio>
    #include<bitset>
    #include<map>
    using namespace std;
    map<int,int>g;
    int n,m,k,cnt=0;
    int nxtC(int k,int lim){
        int ret,b=k&-k,t=(k+b);
        ret=(((t^k)>>2)/b)|t;
        if(ret>=1<<lim)
        return 0;
        return ret;
    }
    void find(int x){ 
        bitset<32>b(x);
        int f[32],top=0,c=m-b.count();
        for(int i=0;i<m;i++)
        if(b[i])f[top++]=i;
        int ik=(1<<k)-1;
        do{
            bitset<32>t;
            for(int i=0;i<top;i++)
            if(ik&(1<<i))
            t[f[i]]=1;
            int y=t.to_ulong();
            if(!g[y]){
                g[y]=x;cnt++;return;
            }
            else{
                int r=g[y];
                bitset<32>tmp(r);
                if(tmp.count()<=m-c)
                continue;
                g[y]=x;
                find(r);
                return;
            }
        }while(ik=nxtC(ik,top));
    }
    int main(){
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++){
            int t;scanf("%d",&t);
            bitset<32>p;
            for(int i=1;i<=t;i++){
                int x;scanf("%d",&x);
                p[x-1]=1;
            }
            if(t<k)continue;
            find(p.to_ulong());
        }
        cout<<cnt;
    }
    

    注:nxtC(int k,int lim)即求出当前为k(int表示的01串)的下一个$inom{K}{lim}$组合。

    如:nxC(11(10102),5)返回的应该是12(11002),即四选二的1010的下一个组合。枚举顺序从小到大。

    注意函数带的参数k并不是组合数里的K。我的变量有的定义在函数里有的是全局,组合数的K是全局的那个,但在nxC函数里是失效的,也就是说nxC函数并不需要知道要填几个1,因为根据上一个状态就能知道有几个1了。


    9.6更

    昨天向Claris要到了代码!!鸡冻!!

    我的做法貌似思想是没有错的,出现问题的部分其实是完全可删的。并不需要那个不正确的贪心来稳定复杂度,因为数据其实很小。而枚举下一状态的时候其实放在匈牙利的dfs函数里就行,Claris代码的框架就是:匈牙利的dfs,在判断dfs(match[已经占过这个状态的牛])的时候重新从初状态开始枚举,也就是把深搜枚举下一状态的dfs嵌套在匈牙利的dfs中。实在是喵啊!额复杂度我并不会分析。。

  • 相关阅读:
    工作中遇到新知识应该怎么办
    Java中的集合
    JSTL学习(二)自定义标签库
    别跟我扯依赖注入
    经典算法的分析
    Debian
    C 底层细节【转】
    C文件操作 【转】
    利用strstr和sscanf解析GPS信息
    算法学习建议 ACM()转
  • 原文地址:https://www.cnblogs.com/orzzz/p/7478429.html
Copyright © 2020-2023  润新知