您的位置:首页 > 其它

POJ 2923 dp 状态压缩

2017-08-23 00:00 381 查看
dp

/*
POJ  2923
解法为状态压缩DP+背包,
本题的解题思路是先枚举选择若干个时的状态,
总状态量为1<<n,判断这些状态集合里的那些物品能否一次就
运走,如果能运走,那就把这个状态看成一个物品。预处理完能
从枚举中找到tot个物品,再用这tol个物品中没有交集
(也就是两个状态不能同时含有一个物品)的物品进
行01背包,每个物品的体积是state[i],价值是1,求
包含n个物品的最少价值也就是dp[(1<<n)-1](dp[i]表示状态i需要运的最少次数)。

状态转移方程:dp[j|k] = min(dp[j|k],dp[k]+1) (k为state[i,1<=j<=(1<<n)-1])。
算法复杂度O((2^N)*N)

*/

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int state[1030];
int tol;
int dp[1030];
int n, C1, C2;
int cost[110];
int vis[1030];
int dp2[1030];

//判断状态是否满足要求,先按照01背包,然后看剩下的第二个是不是可以装走
bool judge2(int x){
memset(dp2, 0, sizeof(dp2));
memset(vis, 0, sizeof(vis));
int cnt = 1, sum = 0;
for (int i = 0; i < n; i++)
if (x & (1 << i)){
vis[cnt++] = cost[i], sum += cost[i];
}
if (sum > C1 + C2)
return 0;
for (int i = 1; i <= cnt; i++)
dp2[i] = 0;

for (int i = 1; i <= cnt; i++){
for (int v = C1; v >= vis[i]; v--)
dp2[v] = max(dp2[v], dp2[v - vis[i]] + vis[i]);
}

if (dp2[C1] + C2 < sum)
return 0;
else return 1;
}

//判断x是否满足状态
bool judge(int x){
int sum = 0;
memset(vis, 0, sizeof(vis));
//第一辆车可以不装
vis[0] = true;
//遍历每一个位置
for (int i = 0; i < n; i++){
if ((1 << i) & x){
sum += cost[i];
//剩余空间可以装下第i个东西
for (int j = C1; j >= cost[i]; j--)
if (vis[j - cost[i]])   //如果放入后的状态可行,则该状态可行
vis[j] = true;
}
}

//如果总数太多
if (sum > C1 + C2)
return false;

//遍历第一辆车可以装的数目,如果第一辆车可以装这么多,并且剩下的第二辆车也能装下
for (int i = 0; i <= C1; i++)
if (vis[i] && sum - i <= C2)
return true;
return false;
}
int main(){
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);

int T;
int iCase = 0;
scanf("%d", &T);
while (T--){
iCase++;
scanf("%d%d%d", &n, &C1, &C2);
for (int i = 0; i < n; i++)
scanf("%d", &cost[i]);
for (int i = 0; i < (1 << n); i++) dp[i] = INF;
dp[0] = 0;
tol = 0;

for (int i = 1; i < (1 << n); i++)
if (judge(i))
state[tol++] = i;

for (int i = 0; i < tol; i++)
for (int j = (1 << n) - 1; j >= 0; j--){
if (dp[j] == INF) continue;
if ((j & state[i]) == 0){
dp[j | state[i]] = min(dp[j | state[i]], dp[j] + 1);
}
}
printf("Scenario #%d:\n%d\n\n", iCase, dp[(1 << n) - 1]);
}
return 0;
}


记忆化搜索

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#define LL long long
#define myabs(x) ((x) > 0 ? (x) : (-(x)))
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = (1 << 10) + 10;
int f[maxn], goods[maxn];
int damn[110];
int w[15];
int n, c1, c2, tot;
int judge(int x){
int i, j;
memset(damn, 0, sizeof(damn));
int sum = 0;
for (i = 0; (1 << i) <= x; i++){
if (x & (1 << i)){
sum += w[i + 1];
for (j = c1; j >= w[i +
7fe0
1]; j--)
damn[j] = max(damn[j], damn[j - w[i + 1]] + w[i + 1]);
}
}
if (sum - damn[c1] <= c2) return 1;
else return 0;
}
int dfs(int sta){
if (f[sta] != -1) return f[sta];
if (sta == 0) return 0;
int i;
f[sta] = inf;
int tem;
for (i = 0; i < tot; i++){
if (sta >= goods[i] && ((sta - goods[i]) & goods[i]) == 0){
tem = dfs(sta - goods[i]) + 1;
if (tem < f[sta])
f[sta] = tem;
}
}
return f[sta];
}
int main(){
int T;
cin >> T;
int cas = 0;
while (T--){
scanf("%d%d%d", &n, &c1, &c2);
int i;
tot = 0;
for (i = 1; i <= n; i++)
scanf("%d", &w[i]);
for (i = 1; i < (1 << n); i++)
if (judge(i)) goods[tot++] = i;
memset(f, -1, sizeof(f));
int ans = dfs((1 << n) - 1);
int j;
printf("Scenario #%d:\n%d\n\n", ++cas, ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dp 状态压缩 背包