两道状态压缩DP-- POJ 3254,HDU 1074

Corn Fields POJ - 3254

农夫有一块地要养牛,有草的地方才能放牛且牛不能相邻放置,求不同放法的总数。需要用状态压缩的方法保存每一种放置的状态,例如二进制101表示第一格和第三个放置了牛,转换为10进制就是5,于是每一个数字就对应了一种放置状态。1.首先处理出每一排那些状态不会有牛同排相邻,例如011(2进制)=3(10进制)就有两头牛相邻,所以(i&(i << 1)) == 0即该状态同排不会有牛相邻。2.将map有草的地方对应的二进制位置设为0,其余为1,则当某种放法全放在草地上,则该状态的十进制a&map==0。3.对于相邻的两行的状态a和b,若a&b==0则这相邻两行的这两种状态不存在相邻的牛,该情况可行。

#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int MAXN = 1 << 13;
int map[13], f[MAXN];
int dp[13][MAXN];//dp[x][y]第x行状态为y时的情况总和
int n, m;

int main()
{
    int x;
        scanf("%d%d", &n, &m);
        memset(map, 0, sizeof(map));
        for (int i = 1; i <= n; i++){
            for (int j = 0; j < m; j++){
                scanf("%d", &x);
                if (x == 0)//能放的为0,不能放的为1
                    map[i] += (1 << j);
            }
        }
        int kk = -1;
        int upper = (1 << m) - 1;//所有的状态0表示全都不放,最大的表示全放
        for (int i = 0; i <= upper; i++){//判断哪几种方法不会有同行相邻的
            if ((i&(i << 1)) == 0)
                f[++kk] = i;
        }
        memset(dp, 0, sizeof(dp));
        for (int i = 0; i <= kk; i++){//判断第一行
            if (!(f[i] & map[1]))
                dp[1][f[i]] = 1;
        }
        for (int i = 2; i <= n; i++){
            for (int j = 0; j <= kk; j++){
                if (!(f[j] & map[i])){//判断这种状态满足出现在该地图吗
                    for (int k = 0; k <= kk; k++){
                        if (!(f[k] & f[j])){//两行与运算为0说明无相邻
                            dp[i][f[j]] += dp[i - 1][f[k]];
                        }
                    }
                }
            }
        }
        int ans = 0;
        for (int i = 0; i <= kk; i++){
            ans += dp[n][f[i]];
        }
        printf("%d\n", (ans % 100000000));
    return 0;
}

Doing Homework HDU - 1074

每科作业每超出期限一天,就扣一分,问如何安排顺序,使得总扣分数最小。用状态压缩的方法保存哪些作业做完了,二进制位为1表示作业完成了,0表示还没做。从小到大枚举每种作业完成状态,然后枚举作业,递推得出最优解。

#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int MAXN = 1 << 16;
struct Node
{
    int cost;//已用时间
    int pre;//前一状态
    int reduced;//最少损失的分数 
}dp[MAXN];
bool visited[MAXN];
struct Course
{
    int deadtime;//截止日期
    int cost;//所需日期
    char name[201];
}course[16];
void output(int status)
{
    int curjob = dp[status].pre^status;
    int curid = 0;
    curjob >>= 1;
    while (curjob)
    {
        curid++;
        curjob >>= 1;
    }
    if (dp[status].pre != 0)
    {
        output(dp[status].pre);
    }
    printf("%s\n", course[curid].name);
}
int main()
{
    int T, n, i, j;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        int tupper = (1 << n)-1;
        for (i = 0; i<n; i++)
        {
            scanf("%s%d%d", &course[i].name, &course[i].deadtime, &course[i].cost);
        }
        memset(visited, false, sizeof(visited));
        dp[0].cost = 0;
        dp[0].pre = -1;
        dp[0].reduced = 0;
        visited[0] = true;
        for (j = 0; j<tupper; j++)//遍历所有状态 
        {
            for (int work = 0; work<n; work++)
            {
                int cur = 1 << work;
                if ((cur&j) == 0)//该项作业尚未做过
                {
                    int curtemp = cur | j;
                    int day = dp[j].cost + course[work].cost;
                    dp[curtemp].cost = day;
                    int reduce = day - course[work].deadtime;
                    if (reduce<0)reduce = 0;
                    reduce += dp[j].reduced;
                    if (visited[curtemp])//该状态已有访问信息
                    {
                        if (reduce<dp[curtemp].reduced)
                        {
                            dp[curtemp].reduced = reduce;
                            dp[curtemp].pre = j;
                        }
                    }
                    else //没有访问过
                    {
                        visited[curtemp] = true;
                        dp[curtemp].reduced = reduce;
                        dp[curtemp].pre = j;
                    }
                }
            }
        }
        printf("%d\n", dp[tupper].reduced);
        output(tupper);//递归输出 
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值