在百度之星的复赛上对这道题没什么思路,后来看到题解说这是一道状压DP,正好之前对于状压DP知识粗略的了解过一点,借这道题正好好好学习一下状压DP。
这是我学习状压DP看的一些资料:
https://wenku.baidu.com/view/84164e1a227916888486d7d6.html
https://wenku.baidu.com/view/8b3ffc6314791711cc7917a7.html?re=view
题意:在一个图上有N个点,有的点被标记位高点,有的点被标记位低点,然后图中有M条边,问这个图最多可以找到多少个三元组<X,Y, Z>,其中X,Z是高点,Y是低点,并且XY,YZ之间有边,并且每一个点只能存在于一个三元组中(初看题意就是一道匹配题)
分析:因为说给的N的数据量为30,看起来不像状压,但是只要状压高点的状态即可,因为高点最多就只有15个,显然可以状压,然后怎么状态转移呢,可以一次选取低点P,对于每一个高点的状态X,X的每一位代表高点是否被选取,1为已经选取,0为未被选取,对于这样的一个X,如果P和两个高点相连,并且这两个高点没有被选取,那么就可以转移到选取这两个高点之后的状态并且数值加1
状态转移的过程为:
if(!(j >> pp & 1) && !(j >> pq & 1))
{
int tmp = j | (1 << pp);
tmp = tmp | (1 << pq);
dp[i + 1][tmp] = max(dp[i + 1][tmp], dp[i][j] + 1);
ans = max(ans, dp[i + 1][tmp]);
}
这是选取,当然也可以不选取那就保持X状态进入下一轮即可:
//这一轮可以什么都不选,这很重要,一开始没考虑到WA了2次
dp[i + 1][j] = max(dp[i + 1][j], dp[i][j]);
ans = max(ans, dp[i + 1][j]);
同时,将每一次更新的值与结果去最大值,这样就可以得到最大匹配数。
其实,分析下来,这种思路就是01背包的思路,可以换成01背包来写,背包的容量就是2^k - 1(每一个高点都被选取)
AC代码:
#include <iostream>
#include <cstring>
#include <cmath>
#include <queue>
#include <stack>
#include <list>
#include <map>
#include <set>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 16;
int dp[35][(1 << MAXN) + 10];
bool graph[35][35];
bool vertex[35];
int n, m, k;
int main()
{
int CASE;
scanf("%d", &CASE);
while(CASE--)
{
memset(dp, 0, sizeof(dp));
memset(graph, 0, sizeof(graph));
memset(vertex, 0, sizeof(vertex));
scanf("%d%d%d", &n, &m, &k);
int u, v;
for(int i = 0; i < m; ++i)
{
scanf("%d%d", &u, &v);
graph[u][v] = graph[v][u] = true;
}
for(int i = 0; i < k; ++i)
{
scanf("%d", &v);
vertex[v] = true;
}
int point[35] = {0}, lowpoint[35] = {0};
int high = 0, low = 0;
for(int i = 1; i <= n; ++i)
{
if(!vertex[i])
lowpoint[low++] = i;
else
point[i] = high++;
}
int ans = 0;
for(int i = 0; i < low; ++i)
{
u = lowpoint[i];
for(int j = 0; j < (1 << k); ++j)
{
//这一轮可以什么都不选,这很重要,一开始没考虑到WA了2次
dp[i + 1][j] = max(dp[i + 1][j], dp[i][j]);
ans = max(ans, dp[i + 1][j]);
for(int p = 1; p <= n; ++p)
{
if(graph[u][p] && vertex[p])
{
for(int q = p + 1; q <= n; ++q)
{
if(graph[u][q] && vertex[q])
{
int pp = point[p];
int pq = point[q];
if(!(j >> pp & 1) && !(j >> pq & 1))
{
int tmp = j | (1 << pp);
tmp = tmp | (1 << pq);
dp[i + 1][tmp] = max(dp[i + 1][tmp], dp[i][j] + 1);
ans = max(ans, dp[i + 1][tmp]);
}
}
}
}
}
}
}
printf("%d\n", ans);
}
return 0;
}