【HDU6149】Valley Numer II(状压DP)

来源:互联网 发布:三角洲特种部队mac版 编辑:程序博客网 时间:2024/05/18 04:53

在百度之星的复赛上对这道题没什么思路,后来看到题解说这是一道状压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;}


原创粉丝点击