状态压缩

题目描述:

编号为0~n-1的n个位置上分别有价值为w[0]~w[n-1]的n个物品,现在要选出一些物品,使得获得的价值最大,约束条件是:任意连续的m个物品中选取的物品数量不超过q。其中n<=1000, 2<=m<=10, 1<=q<=m, w[i]<=100.

算法分析:

状态定义:

dp[i][s]表示:解决了物品0~i的选取问题,物品i-m+1, i-m+2, ..., i 的选取状态为s时,所获得的最大价值, 其中s的第0, 1, ..., m-1位分别指示物品i-m+1, i-m+2, ..., i的选取情况,对应位为1表示选取,为0表示不选取。

状态转移:

1.如果bitcnt(s)>q, 那么由s不可能转移出任何有效状态.

2.如果bitcnt(s)<=q, 那么此时分为两种情况(设下一状态为ns):

  a.当不选取下一个物品时,ns=s>>1, 有转移方程:dp[i+1][ns] = max(dp[i+1][ns], dp[i][s]);

  b.当选取下一个物品时,ns=(s>>1)+(1<<(m-1)), 在bitcnt(ns)<=q的条件下,有转移方程:dp[i+1][ns] = max(dp[i+1][ns], dp[i][s]+w[i+1]).

我的代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 #define MAXN 1100
 6
 7 int w[MAXN], dp[MAXN][MAXN];
 8
 9 int bitcnt(int x)
10 {
11     int cnt = 0;
12     while(x)
13     {
14         if(x&1)    ++cnt;
15         x = x>>1;
16     }
17     return cnt;
18 }
19
20 int main()
21 {
22     int n, m, q, ans;
23     while(scanf("%d%d%d", &n, &m, &q)!=EOF)
24     {
25         for(int i=0; i<n; ++i)
26             scanf("%d", &w[i]);
27         ans = 0;
28         if(n<m)
29         {
30             for(int s=0; s<(1<<n); ++s)
31             {
32                 if(bitcnt(s)<=q)
33                 {
34                     int sum=0;
35                     for(int j=0; j<n; ++j) if((s>>j)&1) sum += w[j];
36                     ans = max(ans, sum);
37                 }
38             }
39             printf("%d\n", ans);
40             return 0;
41         }
42
43         memset(dp, 0, sizeof(dp));
44         for(int s=0; s<(1<<m); ++s)
45         {
46             if(bitcnt(s)>q) continue;
47             int sum = 0;
48             for(int j=0; j<m; ++j) if((s>>j)&1) sum += w[j];
49             dp[m-1][s] = sum;
50         }
51         for(int i=m-1; i<n-1; ++i)
52         {
53             for(int s=0; s<(1<<m); ++s)
54             {
55                 int bcnt = bitcnt(s);
56                 int ns;
57                 if(bcnt>q) continue;
58                 ns = s>>1;
59                 dp[i+1][ns] = max(dp[i+1][ns], dp[i][s]);
60                 ns = (s>>1)+(1<<(m-1));
61                 bcnt = bitcnt(ns);
62                 if(bcnt>q) continue;
63                 dp[i+1][ns] = max(dp[i+1][ns], dp[i][s]+w[i+1]);
64             }
65         }
66         for(int s=0; s<(1<<m); ++s) ans = max(ans, dp[n-1][s]);
67         printf("%d\n", ans);
68     }
69     return 0;
70 }

题目来源:http://hihocoder.com/problemset/problem/1044?sid=252822

时间: 2024-08-27 07:05:46

状态压缩的相关文章

胜利大逃亡(续)(状态压缩bfs)

胜利大逃亡(续) Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 7357    Accepted Submission(s): 2552 Problem Description Ignatius再次被魔王抓走了(搞不懂他咋这么讨魔王喜欢)……这次魔王汲取了上次的教训,把Ignatius关在一个n*m的地牢里,并在地牢的某些地方安装了带

uva 818(dfs+图+状态压缩)

题意:有n个环,编号从1到n,给出了一些环环相扣的情况,比如给a和b表示a和b两个环的扣在一起的,每个环都是可以打开的,问最少打开多少个环,然后再扣好,可以让所有的环成为一条链. 题解:状态压缩把所有的打开环的情况枚举出来,然后拿去判断是否成立,更新打开环后的图g[i][j],和每个点的度数,不成立有三种情况,1.计算没有打开的环的度数,如果大于2说明不会有链,2.把没有打开环拿去dfs,访问过就vis[i]++,如果vis[i]>=2说明存在环,3.如果打开的环数num + 1小于链的数量,说

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

dp状态压缩

dp状态压缩 动态规划本来就很抽象,状态的设定和状态的转移都不好把握,而状态压缩的动态规划解决的就是那种状态很多,不容易用一般的方法表示的动态规划问题,这个就更加的难于把握了.难点在于以下几个方面:状态怎么压缩?压缩后怎么表示?怎么转移?是否具有最优子结构?是否满足后效性?涉及到一些位运算的操作,虽然比较抽象,但本质还是动态规划.找准动态规划几个方面的问题,深刻理解动态规划的原理,开动脑筋思考问题.这才是掌握动态规划的关键. 动态规划最关键的要处理的问题就是位运算的操作,容易出错,状态的设计也直

HDU3001(KB2-J 状态压缩dp)

Travelling Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 8103    Accepted Submission(s): 2642 Problem Description After coding so many days,Mr Acmer wants to have a good rest.So travelling is

2017盛大游戏杯 零件组装(状态压缩DP之巧妙枚举子集)

题目链接:2017盛大游戏杯 零件组装 题意: 有n个零件,给你相邻关系和排斥关系,每两块零件组装起来有一个代价,问最少的代价总和是多少. 题解: 考虑状态压缩,dp[i]表示i这个集合为一个零件块. 那么要枚举一下i的子集.O(3^n). 先要预处理一下每个集合的排斥个数和相邻个数,然后容斥一下就可以了. 1 #include<bits/stdc++.h> 2 #define mst(a,b) memset(a,b,sizeof(a)) 3 #define F(i,a,b) for(int

HDU1565(状态压缩dp)

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

POJ 1753 Flip game状态压缩+广搜

题意有4*4的16个方格,每个方格有黑白两种颜色,每次点击方格后,被点击方格本身及其上下左右的方格都会改变颜色.给出一组状态,求将这组状态变为全白或者全黑至少需要点击几次.若无法达到,则输出Impossible. 样例输入bwwbbbwbbwwbbwww 样例输出4 思路每个方格只有黑白两种颜色,且只有16个方格,因此可以把每个状态看作一个16位的二进制数(状态压缩),2^16=25536,因此可以用int来保存状态.然后就是BFS暴搜,直到状态为0或者65535(全1)为止. 注意点65535

POJ 3311 【状态压缩DP】

题意: 给n个点,给出矩阵代表i到j单向边的距离. 要求,不介意访问每个点的次数,要求访问完每个点,使得路程总和最小. 思路: 由于不介意访问每个点的次数,所以可以先进行FLOYD求出任意两个点之间的最短路,然后就是DP. 同样的,1代表有访问过,0代表没访问过. dp[s][j]代表访问状态为s的情况下最终到达点j的最优值. 枚举状态s中每一个可以到达的点,从所有可能的点中枚举对其进行更新. 关于状态压缩的细节方面,注意从左边数第i位是从0开始数的,所以第i位代表第i+1个点. 然后状态的总数