状态压缩题目小结

1.POJ - 3254 Corn Fields

题目大意:有一个n*m的草地(草地上有的是沼泽),现在要分配牛去上面吃草,要求每头牛不能相邻(不能有公共边),问有多少种分配方案,一头牛都不分配也算一种分配方案

解题思路:这是碰到的第一道比较另类的压缩

1.首先考虑一下,每一行该怎么分配牛才不会让他们相邻,可以用状态压缩,0表示不放牛,1表示放牛,枚举一下有多少种可行的方案并纪录下来

2.接着考虑一下,因为不能相邻,而相邻的行之间又会相互影响。

考虑到第一行是比较特殊的,可以先枚举第一行的解决方案,接下来再枚举其他行。

在枚举其他行的情况时,要考虑和上一行的方案是否能共存,再考虑一下该方案是否可行(有沼泽影响)

如何判断该方案是否可行,可以先预处理一下地图,将地图也压缩成一个二进制数,0表示草地,1表示沼泽,然后将要枚举的方案和该行状态进行与运算,如果相与结果不为0,表示有牛放在了沼泽地,那么方案就不可行了

判断和上一行能否共存的判断和上面判断差不多,用该行的方案和上一行方案进行与运算,如果结果不为0,表示有两头牛放在了同一列,那么方案不可行

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn 20
#define maxm 5010
#define mod 100000000
int R, C;
int row[maxn], state[maxm], dp[maxn][maxm];
int cnt;

void init() {

    memset(row, 0, sizeof(row));
    memset(dp, 0, sizeof(dp));
    int t;
    //处理地图,1表示沼泽
    for (int i = 0; i < R; i++)
        for (int j = 0; j < C; j++) {
            scanf("%d", &t);
            if(!t)
                row[i] |= (1 << j);
        }

    cnt = 0;
    //枚举一下符合的状态,如果i和(i << 1)的与结果不为0,表示有相邻了
    for (int i = 0; i < (1 << C); i++) {
        if (i & (i << 1))
            continue;
        state[cnt++] = i;
    }
    //枚举第一行,
    for (int i = 0; i < cnt; i++) {
        if (state[i] & row[0])
            continue;
        dp[0][i] = 1;
    }

    for (int r = 1; r < R; r++)//枚举第r行
        for (int i = 0; i < cnt; i++) {//枚举第r行的方案
            if (state[i] & row[r]) //判断该行是否支持该方案
                continue;
            for (int j = 0; j < cnt; j++) {//枚举第r-1行的方案
                if (state[j] & row[r-1])
                    continue;
                if (state[i] & state[j])//判断行之间知否有相邻的牛
                    continue;
                dp[r][i] = (dp[r-1][j] + dp[r][i]) % mod;
            }
        }
}

int solve() {
    int ans = 0;
    for (int i = 0; i < cnt; i++)
        ans = (ans + dp[R-1][i]) % mod;
    return ans;
}

int main() {
    scanf("%d%d", &R, &C);
    init();
    printf("%d\n", solve());
    return 0;
}

2.POJ - 1185 炮兵阵地

这题和上题差不多,只不过是多了一个判断

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn 110
#define maxm 20
#define maxs 70
int R, C, cnt;
char str[15];
int row[maxn], num[maxn], statu[maxn];
int dp[maxn][maxs][maxs];

void input() {

    memset(row, 0, sizeof(row));
    for(int i = 0; i < R; i++) {
        scanf("%s", str);
        for(int j = 0; j < C; j++)
            if(str[j] == ‘H‘)
                row[i] += (1 << j);
    }
}

void init_statu() {

    memset(num, 0, sizeof(num));
    cnt = 0;
    for(int i = 0; i < (1 << C); i++) {
        if((i & (i << 1)) || (i & (i << 2)))
            continue;
        int t = i;
        while(t) {
            num[cnt] += (t & 1);
            t >>= 1;
        }
        statu[cnt++] = i;
    }
}

void init_DP() {

    memset(dp, 0, sizeof(dp));
    for(int i = 0; i < cnt; i++) {
        if(statu[i] & row[0])
            continue;
        dp[0][i][0] = num[i];
    }

    for(int i = 0; i < cnt; i++)  {
        if(statu[i] & row[1])
            continue;

        for(int j = 0; j < cnt; j++) {
            if(statu[j] & row[0])
                continue;
            if(statu[i] & statu[j])
                continue;
            dp[1][i][j] = max(dp[1][i][j], dp[0][j][0] + num[i]);
        }
    }
}

int solve() {
    for(int r = 2; r < R; r++) {
        for(int i = 0; i < cnt; i++) {
            if(statu[i] & row[r])
                continue;
            for(int j = 0; j < cnt; j++) {
                if(statu[j] & row[r - 1])
                    continue;
                if(statu[i] & statu[j])
                    continue;
                for(int k = 0; k < cnt; k++) {
                    if(statu[k] & statu[j])
                        continue;
                    if(statu[k] & statu[i])
                        continue;
                    if(statu[k] & row[r - 2])
                        continue;
                    dp[r][i][j] = max(dp[r][i][j], dp[r - 1][j][k] + num[i]);
                }
            }
        }
    }
    int ans = 0;
    for(int i = 0; i < cnt; i++)
        for(int j = 0; j < cnt; j++)
            ans = max(ans, dp[R-1][i][j]);
    return ans;
}

int main() {
    while(scanf("%d%d", &R, &C) != EOF) {
        input();
        init_statu();
        init_DP();
        printf("%d\n", solve());
    }
    return 0;
}

3.POJ - 3311 Hie with the Pie

题目大意:有一个人要送披萨到N个地方,同一个地方可以去多次,问送完所有披赛再回到店里走的最短路径是多少

解题思路:可以将所有的地方压缩成一个状态,0表示还没有经过,1表示已经经过了,然后再bfs就可以求得结果了

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define N 15
#define maxn 1050
#define INF 0x3f3f3f3f

using namespace std;
struct DP{
    int num, state;
}start;
int n, g[N][N];
int dp[N][maxn];

void init(){ 

    int t;
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n ; j++) {
            scanf("%d", &t);
            g[i][j] = t;
        }
    memset(dp, 0x3f, sizeof(dp));
    start.num = 0;
    start.state = 1;
    dp[0][1] = 0;
}

int solve() {
    queue<DP> q;
    q.push(start);

    while(!q.empty()) {
        DP t = q.front();
        q.pop();
        for(int i = 0; i < n; i++) {
            if(i == t.num)
                continue;
            if(dp[i][t.state | (1 << i)] > dp[t.num][t.state] + g[t.num][i]) {
                dp[i][t.state | (1 << i)] = dp[t.num][t.state] + g[t.num][i];
                DP tt;
                tt.num = i;
                tt.state = (t.state) | (1 << i);
                q.push(tt);
            }
        }
    }

    return dp[0][(1 << n) - 1];
}

int main() {
    while(scanf("%d", &n) != EOF && n) {
        n++;
        init();
        printf("%d\n", solve());
    }
    return 0;
}

4.ZOJ - 3471 Most Powerful

题目大意:有n个原子,两个原子相互碰撞的话,就会产生能量,并且另一个原子会消失,问n个原子能产生的最大能量是多少

解题思路:注意这题,能量有可能是负的,所以不需要每个原子都用掉。

用0表示没用,1表示用了,进行压缩

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 15
#define maxn 1200
int power[N][N];
int dp[maxn];
int n;

int main() {
    while(scanf("%d", &n) != EOF && n) {
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++)
                scanf("%d", &power[i][j]);
        memset(dp, 0, sizeof(dp));

        for(int i = 0; i < (1 << n); i++)
            for(int j = 0; j < n; j++) {
                if((i & (1 << j)))
                    continue;
                for(int k = 0; k < n; k++) {
                    if(!(i & (1 << k)) && k != j) {
                        dp[i | (1 << j)] = max(dp[i | (1 << j)], dp[i] + power[k][j]);
                    }
                }
            }
        int ans = 0;
        for(int i = 0; i < (1 << n); i++)
            ans = max(ans, dp[i]);
        printf("%d\n", ans);
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-01 12:36:04

状态压缩题目小结的相关文章

状态压缩dp小结

最近一段时间算是学了一些状态压缩的题目,在这里做个小结吧 首先是炮兵布阵类题目,这类题目一开始给定一个矩形,要求在上面放置炮兵,如果在一格放了炮兵那么周围的某些格子就不能放炮兵,求最大能放置炮兵的数量 poj1185炮兵布阵 hdu2176 炮兵布阵修改版 poj3254 炮兵布阵弱化版 poj1565 方格取数 然后是状态集合类的题目,这类题目给定一个集合的元素,要求重排列以达到最大化收益,即从已知状态推出未知状态,通常需要处理出元素之间的两两对应关系 zoj3471 模板 poj2817 在

HDU 5418——Victor and World——————【状态压缩+floyd】

Victor and World Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/131072 K (Java/Others)Total Submission(s): 891    Accepted Submission(s): 399 Problem Description After trying hard for many years, Victor has finally received a pilot li

状态压缩---UVA6625 - Diagrams &amp; Tableaux

比赛的时候刷出来的第一个状态DP.(期间有点没有把握是状态DP呢.) 题意:题意还是简单的.K行的方格.之后输入L1~LK 代表每一行方格数.在这些往左紧挨的方格子里填上1~N的数字. 其中右边格子的数值会大于等于左边的格子,下边的格子的数值会大于上边的格子. 其中观察一列的数值.会发现一列的数值均是不一样的.而且 N<=7.也就是说我们让1~N的数字填到第一列上.那我们可以按列来进来状态压缩.也就是认为一列就是一个状态. 也就是我们让: 数值 7 6 5 4 3 2 1 二进制0 0 0 0

hdoj 1885 Key Task 【BFS+状态压缩】

题目:hdoj 1885 Key Task 题意:给出一些点,然后有一些钥匙和门,钥匙拿到才可以打开门,问到出口的最短时间. 分析:很明显的广搜 + 状态压缩题目. 坑点: 1:题目没读清楚,以为要把所有的们打开才能出去. AC代码: #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <queue> #include &

状态压缩经典题目(poj1184 nyoj81)

题目描述: 描述 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图.在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格.图上其它白色网格均攻击不到.从

博弈论类题目小结——转载

出处http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove 首先当然要献上一些非常好的学习资料: 基础博弈的小结:http://blog.csdn.net/acm_cxlove/article/details/7854530 经典翻硬币游戏小结:http://blog.csdn.net/acm_cxlove/article/details/7854534 经典的删边游戏小结:http://blog.csdn.net/acm

[poj3904]Sky Code_状态压缩_容斥原理

Sky Code poj-3904 题目大意:给你n个数,问能选出多少满足题意的组数. 注释:如果一个组数满足题意当且仅当这个组中有且只有4个数,且这4个数的最大公约数是1,$1\le n\le 10^4$. 想法:我们显然可以知道4个数是可以不用两两互质的,所以正面计算难度较大,我们考虑从反面考虑.我们通过计算所有gcd不为1的组数,用总组数相减即可.然后,我们发现一个不为0的gcd显然可以被组中的任意一个数整除,所以我们可以进行容斥.只需要枚举gcd的约数个即可.计算的过程我们用状态压缩实现

POJ 3254 Corn Fields 状态压缩DP (C++/Java)

http://poj.org/problem?id=3254 题目大意: 一个农民有n行m列的地方,每个格子用1代表可以种草地,而0不可以.放牛只能在有草地的,但是相邻的草地不能同时放牛, 问总共有多少种方法. 思路: 状态压缩的DP. 可以用二进制数字来表示放牧情况并判断该状态是否满足条件. 这题的限制条件有两个: 1.草地限制. 2.相邻限制. 对于草地限制,因为输入的时候1是可以种草地的. 以"11110"草地分析,就只有最后一个是不可以种草的.取反后得00001  .(为啥取反

uva 11195 Another queen (用状态压缩解决N后问题)

题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2136 Problem A Another n-Queen Problem I guess the n-queen problem is known by every person who has studied backtracking. In this problem you s