Watch Dogs | ||||||
| ||||||
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]);
}
}