题意
给出一张图,有若干点之间连边,边权 ∈ { 0 , 1 } \in\{0,1\} ∈{0,1},对于点1,与它直接连边的点且边权为0,就是盟友,否则是情敌;间接连边,就要按某种顺序同时列出它们的熟人,如果关系相同,那么这两个人就是盟友,否则是情敌。求点1最多的盟友。
思路
把直接与点1连边的点成为A类点,间接的称为B类点。
70分做法,状态压缩A点选哪些,更新答案。
100分做法,取A,B类点状态小的进行状压dp。
思路
#include <cstdio>
#include <cstring>
#include <algorithm>
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
int n, m, aCnt, bCnt, ans;
int f[1 << 23], id[43];
long long con[43], dev[43], v[43][43];
int main() {
scanf("%d %d", &n, &m);
memset(v, -1, sizeof(v));
for (int i = 1, x, y, op; i <= m; i++) {
scanf("%d %d %d", &x, &y, &op);
v[x][y] = v[y][x] = op;
}
for (int i = 2; i <= n; i++) {
if (v[1][i] == -1) continue;
id[i] = ++aCnt;
ans += v[1][i] ^ 1;
for (int j = 2; j <= n; j++)
if (v[1][j] == -1 && v[i][j] != -1) {
if (!id[j]) id[j] = ++bCnt;
con[id[i] - 1] |= 1 << id[j] - 1;
dev[id[i] - 1] |= (v[1][i] ^ v[i][j] ^ 1) << id[j] - 1;
}
}
if (aCnt < bCnt) {
int S = 1 << aCnt;
memset(f, 0xcf, sizeof(f));
f[0] = 0;
for (int i = 0; i < S; i++) {
if (f[i] < 0) continue;
int k = 0;
for (int j = 0; j < aCnt; j++)
if (i & (1 << j)) k |= con[j];
for (int j = 0; j < aCnt; j++)
if (!(i & (1 << j)))
f[i | (1 << j)] = std::max(f[i | (1 << j)], f[i] + __builtin_popcountll(dev[j] & (~k)));
}
printf("%d", ans + f[S - 1]);
} else {
int S = 1 << bCnt;
memset(f, 0xcf, sizeof(f));
f[0] = 0;
for (int i = 0; i < S; i++) {
if (f[i] < 0) continue;
int k = 0;
for (int j = 0; j < aCnt; j++)
if ((i | con[j]) != i)
f[i | con[j]] = std::max(f[i | con[j]], f[i] + __builtin_popcountll(dev[j] & (~i)));
}
printf("%d", ans + f[S - 1]);
}
}