hrbust 2141 Watch Dogs【状压dp】

Watch Dogs
Time Limit: 15000 MSMemory Limit: 32768 K
Total Submit: 2(2 users)Total Accepted: 2(2 users)Rating: Special Judge: No
Description

在未来的芝加哥,中央控制系统(ctOS)监控着全城,我们的主角Aiden—— 一个黑客通过黑入这个系统来打击犯罪。一天,Aiden的妹妹被绑架了,罪犯们把她关进一个屋子里,Aiden要解救妹妹并杀光所有罪犯,但他要先知道所有罪犯的位置,所以他要用手机(好腻害的手机)黑入屋里的监控器去看屋内的情况。已知屋子里有m个罪犯(m <= 16,编号从0~m-1),n个监控器(n <=100),每个监控器可以获取不同罪犯的位置。

Input

第一行一个整数 t ,代表有 t 组数据。T<=10

每组数据第一行两个整数 n , m。

接下来 n 行:每行第一个整数s表示第i个监控器监控到的人数,接下来s个整数表示此监控器监控到的罪犯的编号。

Output

如果Aiden能确定所有罪犯的位置,每行输出他最少需要入侵的监控器的个数,否则输出none。

Sample Input

1

4 6

3 1 2 3

4 0 1 5 4

2 0 1

3 2 3 4

Sample Output
2
Source
2014暑假集训练习赛(7月30日)

思路:


涉及到的位运算:&:同位都是1结果为1,其余都为0;


1、设定dp【i】,表示状态为i使用的最少监控器的个数,假如有5个人,00000表示就是所有人都监控到了,11111表示所有人都没有监控到。(我是这样设定的,当然如果你反过来设定也没人拦你)


2、设定a【i】表示监视器i能够监视到的人的状态,比如能够监视0号,2号,3号人员,则a【i】=01101(m==5),在输入的时候维护这个值。


3、那么很容易写出状态转移方程:

dp【q】=dp【i】+1;(q=i-a【j】&i){a【j】&i!=0,就是说状态i能够通过监视器j监视到状态i没有监视到的人,我们才进行状态转移。}

那么我们初始化设定dp【(1<<m)-1】=0;并且确定dp方向:从(1<<m)-1,向0方向转移。

然后第一层for枚举当前状态i,第二层for枚举要选择的监视器j,对应得到状态q即可。


Ac代码:


#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
int a[500];
int dp[1<<18];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&n,&m);
        memset(a,0,sizeof(a));
        memset(dp,0x3f3f3f3f,sizeof(dp));
        for(int i=0;i<n;i++)
        {
            int k;
            scanf("%d",&k);
            for(int j=0;j<k;j++)
            {
                int tmp;
                scanf("%d",&tmp);
                a[i]+=(1<<tmp);
            }
        }
        dp[(1<<m)-1]=0;
        for(int i=(1<<m)-1;i>=0;i--)
        {
            for(int j=0;j<n;j++)
            {
                if((a[j]&i)!=0)
                {
                    int tmpp=a[j]&i;
                    int q=i-tmpp;
                    dp[q]=min(dp[q],dp[i]+1);
                }
            }
        }
        if(dp[0]==0x3f3f3f3f)printf("none\n");
        else
        printf("%d\n",dp[0]);
    }
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值