• 【状压DP】【UVA11795】 Mega Man's Mission


    传送门

    Description

      你要杀n个怪,每杀掉一个怪那个怪会掉落一种武器,这种武器可以杀死特定的怪。游戏初始你有一把武器,能杀死一些怪物。每次只能杀一只,求有多少种杀怪方法。

    Input

      多组数据,第一行是数组组数T,对于每组数据,有:

    • 第一行是怪物个数n
    • 第二行以0/1串的形式描述初始武器能杀死的怪物
    • 下面n行,第i行以0/1串的形式描述干掉第i只怪以后掉落武器能杀死的怪物

    Output

      对于每组数据,输出:

    • 杀怪的顺序数,形式为Case X: Y

    Sample Input

    3
    1
    1
    1
    2
    11
    01
    10
    3
    110
    011
    100
    000

    Sample Output

    Case 1: 1
    Case 2: 2
    Case 3: 3

    Hint

    n≤16

    Solution

    看看数据范围,大概是个状压DP。

    记录kldi为集合i在二进制意义下代表的怪物编号被干掉后掉落武器能干掉的怪物的集合。

    由于有一把初始武器,我们可以认为干掉任意集合的怪物都能干掉初始武器能干掉怪物的集合。

    设fi为干掉集合i在二进制意义下代表的怪物的顺序个数

    枚举该集合的每个元素,考虑该元素的补集被干掉后能不能干掉他,若能,则转移。

    转移用到加法原理,方程为fi+=f| j是i中的元素且kld[j^i]&j

    Code

    #include<cstdio>
    #include<cstring>
    #define rg register
    #define ci const int
    #define cl const long long int
    
    typedef long long int ll;
    
    inline void qr(int &x) {
        char ch=getchar(),lst=NULL;
        while(ch>'9'||ch<'0') lst=ch,ch=getchar();
        while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        if (lst=='-') x=-x;
    }
    
    char buf[20];
    inline void write(int x,const char aft,const bool pt) {
        if(x<0) {putchar('-');x=-x;}
        int top=0;
        do {
            buf[++top]=x%10+'0';
            x/=10;
        } while(x);
        while(top) putchar(buf[top--]);
        if(pt) putchar(aft);
    }
    
    template <typename T>
    inline T mmax(const T &a,const T &b) {if(a>b) return a;return b;}
    template <typename T>
    inline T mmin(const T &a,const T &b) {if(a<b) return a;return b;}
    template <typename T>
    inline T mabs(const T &a) {if(a<0) return -a;return a;}
    
    template <typename T>
    inline void mswap(T &a,T &b) {T temp=a;a=b;b=temp;}
    
    const int maxn = 20;
    const int maxt = 131072;
    
    int t,cnt,n;
    ll frog[maxt];
    int gorf[maxn],kld[maxt];
    
    void clear();
    
    int main() {
        qr(t);
        while(t--) {
            clear();
            qr(n);
            for(rg int j=0;j<n;++j) {
                char ch=getchar();while((ch!='1') && (ch!='0')) ch=getchar();
                if(ch=='1') kld[0]|=(1<<j);
            }
            for(rg int i=0;i<n;++i) {
                for(rg int j=0;j<n;++j) {
                    char ch=getchar();while((ch!='1') && (ch!='0')) ch=getchar();
                    if(ch=='1') kld[1<<i]|=(1<<j);
                }
            }
            rg int upceil = (1<<n)-1;
            for(rg int i=0;i<=upceil;++i) {
                for(rg int j=0;j<n;++j) if((1<<j)&i) {
                    kld[i]|=kld[1<<j];
                }
                kld[i]|=kld[0];
            }
            frog[0]=1;
            for(rg int i=1;i<=upceil;++i) {
                for(rg int j=0;j<n;++j) if(i&(1<<j)){
                    if(kld[i^(1<<j)]&(1<<j)) frog[i]+=frog[i^(1<<j)];
                }
            }
            printf("Case %d: %lld
    ",++cnt,frog[upceil]);
        }
    }
    
    void clear() {
        n=0;
        memset(kld,0,sizeof kld);
        memset(frog,0,sizeof frog);
        memset(gorf,0,sizeof gorf);
    }

    Summary

    在状压DP进行转移的时候,不一定需要枚举子集进行转移,常用的另一种转移方式是枚举集合中元素进行转移。

  • 相关阅读:
    yum error
    Linux如何查询内存真实利用率
    管理工作
    top 详解
    炒股
    vsftp+apache中文乱码问题
    生活像杯咖啡
    vmware workstation 9 nat setting
    cp文件
    WPF 数据分页控件改进
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/9452690.html
Copyright © 2020-2023  润新知