枚举,集合,动态规划(黑客的攻击,uva 11825)

来源:互联网 发布:淘宝香水嗅觉系真假 编辑:程序博客网 时间:2024/06/05 06:08

很容易想到这是一个集合覆盖问题。

我已开始还脑抽了,用什么强连通分量= =,他只是能中止相邻的,又不是中止一个块,但是样例能过。。。只能说自己有时候真的都没静下心来认认真真想清楚,分析好自己算法的正确性,而是瞎想觉得差不多啦就开始写代码了。

WA后又想了一个错误的解法。自己应该先确定算法,然后在纸上证明正确性,然后纸上跑几个数据,再编码才好。这种瞎想出来的解法,更是要严谨,不能随便脑子里模拟几个简单的例子就觉得对了。


然后如果自己的解法的时间复杂度远小于题目的数据量,那么你还是掂量一下吧。


1<=N<=16


一看就是指数级的时间复杂度。那就跑不了枚举集合啦。

每个点及其它的领点都是一个集合,多个集合的并集可能会覆盖所有点,而且可能覆盖很多次。那么该怎么动态的安排这些集合,让他们覆盖的次数最多呢?

动态规划。

如何选取集合是个问题,那我们就需要枚举集合的集合。下面将小集合叫元素,小集合的集合叫集合。


枚举集S合,再枚举集合的子集S0,如果S0能覆盖所有点,那么一种可行的方案就是让S0瘫痪一个服务,让S^S0去尽量瘫痪最多的服务。S0与S^S0都是S的子集,所以从小到大枚举集合S,然后取方案的最优解就好了,如果没有方案,那就是0。

dp[S]代表集合S最多能瘫痪几个服务。

状态转移方程为dp[S]=max(dp[S-S0]|S0是S的子集,且S0可以覆盖所有点)+1。dp[S]初始化为0,即无方案的情况。

预处理出所有情况是否能覆盖所有点,以O(1)判断。

大白书上的代码真的经典。特别是枚举子集那段太6。


代码

#include<bits/stdc++.h>#define maxn 20#define maxs (1<<16)using namespace std;int N;int P[maxn];int dp[maxs];int cover[maxs];int kase;int CUL(int S){    int ans=0;    for(int i=0;i<N;i++)        if(S&(1<<i))            ans|=P[i];    return ans;}int main(){    while(scanf("%d",&N)==1&&N)    {        for(int i=0;i<N;i++)        {            P[i]=0;            P[i]|=1<<i;            int m,v;            scanf("%d",&m);            for(int j=0;j<m;j++)            {                scanf("%d",&v);                P[i]|=1<<v;            }        }        int ALL=(1<<N)-1;        for(int S=0;S<(1<<N);S++)        {            cover[S]=CUL(S);            dp[S]=0;            for(int S0=S;S0;S0=(S0-1)&S)                if(cover[S0]==ALL) dp[S]=max(dp[S],dp[S^S0]+1);        }        printf("Case %d: %d\n",++kase,dp[(1<<N)-1]);    }    return 0;}


0 0
原创粉丝点击