hdu 2167 方格取数 【状压dp】(经典)

<题目链接>

题目大意:

给出一些数字组成的n*n阶矩阵,这些数字都在[10,99]内,并且这个矩阵的  3<=n<=15,从这个矩阵中随机取出一些数字,在取完某个数字后,该数字周围8个点都不能取,问:取得数字的最大和为多少?

解题分析:

由于对每一个数,有选和不选两种可能,分别对应状态压缩中的1和0,且 n<=15,1<<15不是非常大,因此就可以非常自然的想到状态压缩。

此题要与普通的状压dp不同的是,当某一行取某种方案时,如何求出这种取数的所有取得的数之和,就是下面的bit数组,还有要注意一下输入。

#include<cstdio>                  //此题要掌握的是,当某一行取某种方案时,如何求出这种取数的所有取得的数之和,就是下面的bit数组
#include<iostream>                //还要注意如何读入的格式,挺难的
#include<cstring>
#include <algorithm>
#define N (1<<15)+10
int dp[20][N];//dp表示第i行j方案时获得的最大值
int a[20][20], t[N], cnt, n, bit[N];//cnt存合法方案总数
using namespace std;

void init()                //此题要掌握的技巧是,当某一行取某种方案时,如何求出这种取数的所有取得的数之和,就是下面的bit数组
{
    bit[1] = 1;
    for (int i = 2; i <= n; i++)      //存下2的1~15次方,与之后的状态比较
        bit[i] = bit[i - 1] << 1;     //bit[2]为2的1次方,bit[3]为2的二次方
}

void solve()
{
    int i, j, k, l;
    cnt = 0;

    memset(dp, 0, sizeof(dp));
    for (i = 0; i< (1 << n) - 1; i++)           //用二进制存方案,每行最多有(1<<n)-1中方案,包括不合法方案
    {
        if (((i << 1)&i) == 0)             //判断方案i是否相邻两格均为1
        {
            for (j = 1; j <= n; j++)
                if ((bit[j] & i))         //这个技巧一定要掌握,非常妙***
                    dp[1][i] += a[1][j];   //先处理第一行的所有方案    dp[1][i]表示第一行取第i种方案时,在第一行得到的所有数的总数
            t[++cnt] = i;                //这里的t[]数组也同时表示每一行的合法情况(只用一行中选取的数不相邻这一条件来约束时)
        }
    } //初始化dp数组,为下面的状态转移方程做好递推准备

    for (i = 2; i <= n; i++)
    {
        for (j = 1; j <= cnt; j++)
        {
            int posn = 0;
            for (k = 1; k <= n; k++)
                if (bit[k] & t[j])
                    posn += a[i][k];
            for (l = 1; l <= cnt; l++)                                                             //这里的 t[j]&t[l]就是用来判断第i行取j方案时,是否与它上一行取的数,有相邻的,如果有,则这种方案舍弃
                if ((t[j] & t[l]) == 0 && ((t[l] << 1)&t[j]) == 0 && (t[l] >> 1 & t[j]) == 0)      // t[l]<<1 & t[j] 和 t[l]>>1&t[j] 则是分别判断第i行取的数是否与它右上和左上的数相邻,如果相邻,也舍弃
                    dp[i][t[j]] = max(dp[i][t[j]], posn + dp[i - 1][t[l]]);
        }         //dp[i][t[j]]表示当第i行选第j种方案时,前i行能取的数的最大总和
    }
}

int main()//状态压缩dp,状态最多有(2<<15)-1种
{
    int i, j;
    char str[100];
    while (gets_s(str))
    {
        memset(a, 0, sizeof(a));
        n = 0;
        for (i = 0; i < strlen(str); i += 3) {
            a[1][++n] = (str[i] - ‘0‘) * 10 + (str[i + 1] - ‘0‘);
        }
        for (i = 2; i <= n; i++)
        {
            gets_s(str);
            int m = 1;
            for (j = 0; j < strlen(str); j += 3) {
                a[i][m++] = (str[j] - ‘0‘) * 10 + (str[j + 1] - ‘0‘);
            }
        }
        getchar();
        //以上为读入矩阵,这个输入还是要注意

        init();
        solve();
        int res = 0;
        for (i = 1; i <= cnt; i++)
            if (res<dp[n][t[i]])            //dp[i][j]表示当第i行选第j种方案时,前i行能取的数的最大总和
                res = dp[n][t[i]];
        printf("%d\n", res);
    }
    return 0;
}

2018-07-26

原文地址:https://www.cnblogs.com/00isok/p/9374837.html

时间: 2024-08-13 16:57:07

hdu 2167 方格取数 【状压dp】(经典)的相关文章

POJ 2411 &amp;&amp; HDU 1400 Mondriaan&#39;s Dream (状压dp 经典题)

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12341   Accepted: 7204 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series

HDU 1565 方格取数(1) (状态压缩DP)

HDU 1565 方格取数(1) (状态压缩DP) ACM 题目地址: HDU 1565 方格取数(1) 题意: 中文. 分析: dp[i][j]表示前i行状态j的最优解. 先预处理出符合条件的数,17000+个(n在20以内). 不过感觉复杂度挺高的会T,但是却能A. 这题的正解应该是最小割,回头补下. 代码: /* * Author: illuz <iilluzen[at]gmail.com> * File: 1565_dp.cpp * Create Date: 2014-09-19 23

hdu 1565 方格取数(1)

这个题网上很多人都说用状态压缩dp来做,我就是觉得状态压缩dp有点那么理解不上啊,不过如果这个题吧相邻的两个格子连起来,那不就是求最大权独立点集吗?奋战了三天,我的第一道最大流题目终于写出来了,高兴啊! #include<map> #include<set> #include<stack> #include<queue> #include<cmath> #include<vector> #include<cstdio> #

hdu 1565 方格取数(2)(网络流之最大点权独立集)

题目链接:hdu 1565 方格取数(2) 题意: 有一个n*m的方格,每个方格有一个数,现在让你选一些数.使得和最大. 选的数不能有相邻的. 题解: 我们知道对于普通二分图来说,最大独立点集 + 最小点覆盖集 = 总点数,类似的,对于有权的二分图来说,有: 最大点权独立集 + 最小点权覆盖集 = 总点权和, 这个题很明显是要求 最大点权独立集 ,现在 总点权 已知,我们只要求出来 最小点权覆盖集 就好了,我们可以这样建图, 1,对矩阵中的点进行黑白着色(相邻的点颜色不同),从源点向黑色的点连一

【网络流】hdu 1569 方格取数(2)

/* 和1565一样: 总点数的权 - 最小覆盖点集 = 最大独立集 -------------------------------------- void add(int u, int v, int f)加边 { e[ct].u = u; e[ct].v = v; e[ct].f = f; next[ct] = first[u]; first[u] = ct++; e[ct].u = v; e[ct].v = u; e[ct].f = 0; next[ct] = first[v]; first

HDU 4114 Disney&#39;s FastPass (状压DP)

题意:给定 n 个区域,然后给定两个区域经过的时间,然后你有 k 个景点,然后给定个每个景点的区域和有票没票的等待时间,从哪些区域能够得到票,问你从景点1开始,最后到景点1,而且要经过看完这k个景点. 析:一个状压DP,dp[s1][s2][i] 表示已经访问了 s1 中的景点,拥有 s2 的票,当前在 i 区域,然后两种转移一种是去下一个景点,另一种是去下一个区域拿票.当时输入,写错了,卡了好长时间.... 代码如下: #pragma comment(linker, "/STACK:10240

网络流 [HDU 1565] 方格取数(1)

方格取数(1) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 5961    Accepted Submission(s): 2268 Problem Description 给你一个n*n的格子的棋盘,每个格子里面有一个非负数.从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数

HDU 4906 Our happy ending (状压DP)

HDU 4906 Our happy ending 题目链接 题意:给定n个数字,每个数字可以是0-l,要选其中一些数字,然后使得和为k,问方案 思路:状压dp,滚动数组,状态表示第i个数字,能组成的数字状态为s的状态,然后每次一个数字,循环枚举它要选取1 - min(l,k)的多少,然后进行状态转移 代码: #include <cstdio> #include <cstring> typedef long long ll; const int N = (1<<20)

hdu 1569 方格取数(2)再解

上次我说用STL超时了,而用数组为0ms,其实不然,这个题STL依然不超时,代码如下 #include<map> #include<set> #include<stack> #include<queue> #include<cmath> #include<vector> #include<cstdio> #include<string> #include<cstring> #include<c