很经典的状态DP,这道题目的经典之处,在于枚举状态时用了一种很巧妙的手段,有效的减少了无用状态的枚举。
for(int j=i;j;j=(j-1)&i) // 这种状态枚举方法有效减少无用状态,程序运行效率提高明显
if(c[j]==(1<<n)-1)
f[i]=max(f[i],f[i^j]+1);
通过上面的枚举,我们不必从1开始一直枚举到相应的状态 i 因为这之中必定有很多状态不符合我们的要求,而通过
j=(j-1)&i
这种方法我们避免了大量的无用状态枚举
状态方程
f(s):用当前状态下的集合最多能停止多少个服务
f(s)=max(f(s),f(s0)+1) 其中s0为s的子集,且状态s0中的电脑,以及与这些电脑相连的电脑是一个全集。
Problem H
Hackers’ Crackdown
Input: Standard Input
Output: Standard Output
Miracle Corporations has a number of system services running in a distributed computer system which is a prime target for hackers. The system is basically a set of N computer nodes with each of them running a set of N services. Note that, the set of services running on every node is same everywhere in the network. A hacker can destroy a service by running a specialized exploit for that service in all the nodes.
One day, a smart hacker collects necessary exploits for all these N services and launches an attack on the system. He finds a security hole that gives him just enough time to run a single exploit in each computer. These exploits have the characteristic that, its successfully infects the computer where it was originally run and all the neighbor computers of that node.
Given a network description, find the maximum number of services that the hacker can damage.
Input
There will be multiple test cases in the input file. A test case begins with an integer N (1<=N<=16), the number of nodes in the network. The nodes are denoted by 0 to N - 1. Each of the following N lines describes the neighbors of a node. Line i (0<=i<N) represents the description of node i. The description for node i starts with an integer m (Number of neighbors for node i), followed by m integers in the range of 0 to N - 1, each denoting a neighboring node of node i.
The end of input will be denoted by a case with N = 0. This case should not be processed.
Output
For each test case, print a line in the format, “Case X: Y”, where X is the case number & Y is the maximum possible number of services that can be damaged.
Sample Input | Output for Sample Input |
3 2 1 2 2 0 2 2 0 1 4 1 1 1 0 1 3 1 2 0 | Case 1: 3 Case 2: 2 |
Problemsetter: Mohammad Mahmudur Rahman
Special Thanks Manzurur Rahman Khan
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
int n;
int com[20];
int f[1<<20];
int c[1<<20];
int main(){
int cs=1;
while(~scanf("%d",&n)){
if(!n) break;
for(int i=0;i<n;i++){
int m;
scanf("%d",&m);
com[i]=(1<<i);
for(int j=0;j<m;j++){
int tmp;
scanf("%d",&tmp);
com[i]=com[i]|(1<<(tmp));
}
}
for(int s=0;s<(1<<n);s++){
c[s]=0;
for(int i=0;i<n;i++)
if((1<<i)&s) c[s]=c[s]|com[i];
}
f[0]=0;
for(int i=1;i<(1<<n);i++){
f[i]=0;
/*for(int j=1;j<=i;j++){
if((j&i)!=j) continue;
if(c[j]==(1<<n)-1)
f[i]=max(f[i],f[i^j]+1);
}*/
for(int j=i;j;j=(j-1)&i) // 这种状态枚举方法有效减少无用状态,程序运行效率提高明显
if(c[j]==(1<<n)-1)
f[i]=max(f[i],f[i^j]+1);
}
printf("Case %d: %d\n",cs++,f[(1<<n)-1]);
}
return 0;
}