状压DP问题

来源:互联网 发布:网络约车平台 计划书 编辑:程序博客网 时间:2024/06/06 10:04

状态压缩·一

题目传送:#1044 : 状态压缩·一

AC代码:

#include <map>#include <set>#include <list>#include <cmath>#include <deque>#include <queue>#include <stack>#include <bitset>#include <cctype>#include <cstdio>#include <string>#include <vector>#include <complex>#include <cstdlib>#include <cstring>#include <fstream>#include <sstream>#include <utility>#include <iostream>#include <algorithm>#include <functional>#define LL long long#define INF 0x7fffffffusing namespace std;int n, m, q;int w[1005];int dp[1005][1030];//dp[i][j]表示选到第i个位置时j状态能够取得的最大值int cnt[1030];//代表每个数的位数上的1的个数int main() {    cnt[0] = 0, cnt[1] = 1;    for(int i = 2; i < 1030; i ++) cnt[i] = cnt[i >> 1] + cnt[i & 1];    scanf("%d %d %d", &n, &m, &q);    for(int i = 1; i <= n; i ++) {        scanf("%d", &w[i]);    }    int ans = 0;    int d = 1 << m;    for(int i = 1; i <= n; i ++) {        for(int j = 0; j < (1 << m); j ++) {            if(cnt[j] <= q) dp[i][j] = max(dp[i-1][j >> 1], dp[i-1][(j >> 1) + (1 << (m - 1))]) + (j & 1) * w[i];            ans = max(ans, dp[i][j]);        }    }    printf("%d\n", ans);    return 0;}



Hackers’ Crackdown

题目传送:UVA - 11825 - Hackers’ Crackdown

AC代码:

#include <map>#include <set>#include <list>#include <cmath>#include <deque>#include <queue>#include <stack>#include <bitset>#include <cctype>#include <cstdio>#include <string>#include <vector>#include <complex>#include <cstdlib>#include <cstring>#include <fstream>#include <sstream>#include <utility>#include <iostream>#include <algorithm>#include <functional>#define LL long long#define INF 0x7fffffffusing namespace std;const int maxn = (1 << 16) + 5;int dp[maxn];//dp[i]表示子集i最多可以分成多少组int cover[maxn];//cover[i]表示若干集合(i中所表示的集合)的并集int n, m;int P[25];//P[i]表示与i相连的数的集合(包括i)int main() {    int cas = 1;    while(scanf("%d", &n) != EOF) {        if(n == 0) break;        for(int i = 0; i < n; i ++) {            scanf("%d", &m);            P[i] = 1 << i;            int t;            while(m --) {                scanf("%d", &t);                P[i] |= (1 << t);//并入集合i            }        }        for(int i = 0; i < maxn; i ++) {            cover[i] = 0;            for(int j = 0; j < n; j ++) {//if判断j是否在i中,是得话就并入                if(i & (1 << j)) cover[i] |= P[j];            }        }        dp[0] = 0;        int tot = (1 << n) - 1;//全集总数        for(int i = 1; i <= tot; i ++) {//依次枚举全集,因为要先算出前一状态才能推出后一状态            dp[i] = 0;            for(int j = i; j; j = (j - 1) & i) {//枚举子集的技巧,重点!                if(cover[j] == tot) {//i的子集j等于全集,则执行状态转移                    dp[i] = max(dp[i], dp[i ^ j] + 1);                }            }        }        printf("Case %d: %d\n", cas ++, dp[tot]);    }    return 0;}



Sharing Chocolate

题目传送:UVALive - 4794 - Sharing Chocolate

WF2010的题。

AC代码:

#include <map>#include <set>#include <list>#include <cmath>#include <deque>#include <queue>#include <stack>#include <bitset>#include <cctype>#include <cstdio>#include <string>#include <vector>#include <complex>#include <cstdlib>#include <cstring>#include <fstream>#include <sstream>#include <utility>#include <iostream>#include <algorithm>#include <functional>#define LL long long#define INF 0x7fffffffusing namespace std;const int maxn = 16;int d[1 << maxn][105];int vis[1 << maxn][105];int sum[1 << maxn];int a[maxn];int n;int bitcount(int x) {    return x == 0 ? 0 : bitcount(x / 2) + (x & 1);}int dp(int s, int x) {//每次递归找出集合为s,宽为x的巧克力是否可以满足要求    if(vis[s][x]) return d[s][x];    vis[s][x] = 1;    int& ans = d[s][x];    if(bitcount(s) == 1) return ans = 1;//此时为边界,即只有一块巧克力的情况,肯定是满足的    int y = sum[s] / x;//另一个边长可以根据这个求得    for(int s0 = (s - 1) & s; s0; s0 = (s0 - 1) & s) {//枚举子集        int s1 = s - s0;        if(sum[s0] % x == 0 && dp(s0, min(x, sum[s0] / x)) && dp(s1, min(x, sum[s1] / x))) return ans = 1;//竖着切(这里假定宽x是竖着的)        if(sum[s0] % y == 0 && dp(s0, min(y, sum[s0] / y)) && dp(s1, min(y, sum[s1] / y))) return ans = 1;//横着切    }    return ans = 0;}int main() {    int cas = 1, x, y;    while(scanf("%d", &n) != EOF) {        if(n == 0) break;        scanf("%d %d", &x, &y);        for(int i = 0; i < n; i ++) scanf("%d", &a[i]);        //计算每个子集的元素的和        memset(sum, 0, sizeof(sum));        for(int s = 0; s < (1 << n); s ++) {            for(int i = 0; i < n; i ++) if(s & (1 << i)) sum[s] += a[i];        }        memset(vis, 0, sizeof(vis));        int ALL = (1 << n) - 1;        int ans;        if(sum[ALL] != x * y) ans = 0;        else ans = dp(ALL, min(x, y));        printf("Case %d: %s\n", cas ++, ans ? "Yes" : "No");    }    return 0;}



0 0
原创粉丝点击