UVA 11825 状态压缩DP

来源:互联网 发布:知乎 性观念 编辑:程序博客网 时间:2024/05/01 16:42

题目:https://cn.vjudge.net/contest/171116#problem/C

N台电脑,现在有N种服务,现在你可以在每台电脑终止一项服务,他和他相邻的电脑都会被关闭,如果一项服务在所有电脑都没运行,该项服务成功被破坏,问最多能破坏几种服务。

把该题转化成数学模型。把一个集合,分割成几个子集,每个子集中挑选几个数,组成集合,能够并成全集。

4

1

1 0

1 3

1 2

如0 1相连接,2 3相连,把 0 2破坏相同服务,即可破坏该服务,1 3破坏另一种,总共2种服务被破坏。

思路:

这道题首先把每个节点能够到达的节点都找出来。首先找到该节点相邻的节点,然后将相邻节点的相邻节点更新到当前节点当中。这里可以用二分法。s[i]初始化为1<<i,表示第i个节点。然后将其相邻节点temp用s[i]|=1<<temp放入,最后sta[i]表示第i个节点能到达的所有点。

接下来是状态转移方程,f[s]=max(f[s],f[s-s0]+1).s0表示s的子集,且能到达所有节点,so能够破坏一个服务。s-s0表示s中剩下的节点。f[s]表示集合s破坏的最多服务数。用s0模拟s的每一个子集,s0=(s0-1)&s就能模拟每个子集。

#include<iostream>#include<stdio.h>#include<algorithm>using namespace std;#define maxn 1<<18int sta[maxn],f[maxn];int main(){int n,m,i,j;int s[18], q = 1;while (~scanf("%d", &n) && n != 0){for (i = 0; i < n; i++){s[i] = 1 << i;scanf("%d", &m);while(m--){int temp;scanf("%d", &temp);s[i] |= (1 << temp);//初始化第i个相邻的节点}}for (i = 0; i < (1 << n); i++)//sta[i]表示和i能够到达的所有相邻节点 dp[i]{sta[i] = 0;for (j = 0; j < n; j++)if (i&(1 << j))sta[i] |= s[j];}f[0] = 0; int all = (1 << n) - 1;for (int s = 1; s <= all; s++)//f[s]表示集合s能够破坏的最多服务数量{f[s] = 0;for (int s0 = s; s0; s0 = (s0 - 1)&s){if (sta[s0] == all)f[s] = max(f[s], f[s^s0] + 1);//这里的解释是当S中的自己S0可以                                                          //的时候,这相对于补集+1}}printf("Case %d: %d\n", q++, f[all]);}return 0;}