Hacker's Crackdown(UVa 11825)状态压缩dp+数学模型

来源:互联网 发布:红蜘蛛教学软件 编辑:程序博客网 时间:2024/06/16 04:55

来自《算法竞赛入门经典训练指南》

1.题目原文

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2925
假如你是一个黑客,侵入了n台计算机(编号为0,1,2,……n-1)的网络。一共有n种服务,每台计算机都运行着这种服务。对于每台计算机,你都可以选择一项服务,终止这台计算机和所有与它相邻的计算机的该项服务(如果一些服务已经停止,会一直停止下去)。目标是让尽量多的服务完全瘫痪(没有任何计算机运行该项服务).

2.解题思路

数学模型是:把n个集合P[1],P[2],……P[n]分成尽量多组,使得每组中所有集合的并集等于全集。对于本题,集合P[i]就是计算机i及其相邻的计算机的集合,每组对应题目中的一项服务。
集合的并集,用按位或;集合的差集,用按位异或。
显然用二进制表示集合。下面用状态压缩dp来解决这道题。
用cover[S]表示若干P[i]的集合S中所有P[i]的并集。
用f(S)表示子集S最多可以分多少组,则f(S)=max(f(S-S0)}+1.其中S0是S的子集,且cover[S0]等于全集。
这题有一个重要的技巧,枚举S的所有子集S0。

如何分析算法的时间复杂度?它等于全集{1,2,3,……,n}的所有子集的个数,显然就是sum{C(n,k)*2^k}=3^n。

3.AC代码

#include <algorithm>#include <cctype>#include <cmath>#include <cstdio>#include <cstdlib>#include <cstring>#include <iomanip>#include <iostream>#include <map>#include <queue>#include <string>#include <set>#include <vector>#include<cmath>#include<bitset>#include<sstream>#include<stack>using namespace std;#define INF 0x7ffffffftypedef long long ll;const int maxn=(1<<16)+10;//用cover[S]表示若干P[i]的集合S中所有P[i]的并集。//用f(S)表示子集S最多可以分多少组int P[maxn],cover[maxn],f[maxn];int n,m;int main(){    int kase=0;    while(scanf("%d",&n)!=EOF&&n){        for(int i=0;i<n;i++){            scanf("%d",&m);            P[i]=1<<i;            while(m--){                int x;                scanf("%d",&x);                P[i]|=(1<<x);            }        }        for(int S=0;S<(1<<n);S++){            cover[S]=0;            for(int i=0;i<n;i++){                if(S&(1<<i)){                    cover[S]|=P[i];                }            }        }        f[0]=0;        int All=(1<<n)-1;        for(int S=1;S<(1<<n);S++){            f[S]=0;            //枚举集合S的所有子集            for(int S0=S;S0;S0=(S0-1)&S){                if(cover[S0]==All){                    f[S]=max(f[S],f[S^S0]+1);                }            }        }        printf("Case %d: %d\n",++kase,f[All]);    }    return 0;}


0 0