UVa 11795 Mega Man's Mission(集合动态规划)

本文解析了 UVA 在线评测系统中编号为 2895 的问题,通过动态规划方法求解消灭所有机器人的不同方式的数量。采用二进制状态压缩与预处理技术实现高效计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文出自:http://blog.youkuaiyun.com/dr5459

题目地址:

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2895

题目意思:

告诉你初始时可以杀掉那些机器人

你每杀掉一个机器人,就可以拿起他的武器,从而可以杀掉他的武器可以杀掉的机器人

问你杀掉全部的机器人有多少种方案

解题思路:

我们用集合S来表示杀了的机器人,二进制的1即为干掉了,0即为没干掉

那么用dp[s]就可以表示状态s的种数

那么怎么来转移呢?

首先,对于每一种状态,我们预处理一下这个状态下可以干掉哪些机器人,这样才能转移

然后对于一个状态S来说,要干掉第i个机器人,则要从没干掉i的状态里面转移,即s^(1<<i)

则dp[s] = sum(dp[s^(1<<i)])    条件为s&(1<<i)   而且s^(1<<i)可以干掉i

最后加起来就OK啦

代码:

<pre name="code" class="cpp">#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;

const int maxn = 17;
int attack[maxn];       //表示每个机器人能干掉的
int can_attack[1<<16];  //表示每个状态能干掉的
long long dp[1<<16];    //用来统计每种状态的种数
char s[maxn];
int n;


int main()
{
    int T;
    int ca = 1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=0;i<=n;i++)
        {
            attack[i] = 0;
            scanf("%s",s);
            for(int j=0;j<n;j++)
            {
                if(s[j]=='1')
                    attack[i] |= (1<<j);
            }
        }

        int total = (1<<n)-1;
        for(int st=0;st<=total;st++)
        {
            can_attack[st] = attack[0];
            //对每个机器人来说,如果状态可以干掉i,那么也就是可以干掉i所能干掉的
            for(int i=1;i<=n;i++)
            {
                int j=i-1;
                if(st&(1<<j))
                    can_attack[st] |= attack[i];
            }
        }

        //枚举状态
        memset(dp,0,sizeof(dp));
        dp[0]=1;
        for(int st=1;st<=total;st++)
        {
            for(int i=0;i<n;i++)
            {
                if(st & (1<<i))//如果st的这种状态能够干掉i,
                                //那么要由不能干掉i的状态转移过来,即st^(1<<i)
                                //而且st^(1<<i)这种状态要能够干掉i
                {
                    if(can_attack[st^(1<<i)]&(1<<i))
                        dp[st] += dp[st^(1<<i)];
                }
            }
        }

        cout<<"Case "<<ca++<<": "<<dp[total]<<endl;


    }
    return 0;
}



                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值