题意:
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6149
中文题。
思路:
状态压缩,将高点的选择情况压位。dp[i][S]表示的是枚举到第i个低点,且当前选择高点状态为S的最大山谷数。转移简单,注意细节。
代码:
#include <bits/stdc++.h>
using namespace std;
bool g[50][50], vis[50];
int dp[50][(1 << 15) + 10], high[20], low[50];
vector <pair <int, int> > vec[30];
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
memset(g, 0, sizeof g);
for (int i = 1; i <= m; ++i) {
int u, v;
scanf("%d%d", &u, &v);
g[u][v] = g[v][u] = 1;
}
memset(vis, 0, sizeof vis);
for (int i = 0; i < k; ++i) {
scanf("%d", &high[i]);
vis[high[i]] = 1;
}
int cnt = 0;
for (int i = 1; i <= n; ++i) {
if (!vis[i]) low[cnt++] = i;
}
for (int i = 0; i < cnt; ++i) {
vec[i].clear();
for (int j = 0; j < k; ++j) {
if (low[i] == high[j] || !g[low[i]][high[j]]) continue;
for (int t = j + 1; t < k; ++t) {
if (low[i] == high[t] || !g[low[i]][high[t]]) continue;
vec[i].push_back(make_pair(j, t));
}
}
}
memset(dp, 0, sizeof dp);
dp[0][0] = 0;
for (int i = 0; i < cnt; ++i) {
for (int S = 0; S < (1 << k); ++S) {
dp[i + 1][S] = max(dp[i + 1][S], dp[i][S]);
for (int j = 0, sz = vec[i].size(); j < sz; ++j) {
int u = vec[i][j].first, v = vec[i][j].second;
if ((S & (1 << u)) || (S & (1 << v))) continue;
dp[i + 1][S | (1 << u) | (1 << v)] = max(dp[i + 1][S | (1 << u) | (1 << v)], dp[i][S] + 1);
}
}
}
int ans = 0;
for (int S = 0; S < (1 << k); ++S)
ans = max(ans, dp[cnt][S]);
printf("%d\n", ans);
}
return 0;
}