状压dp学习笔记

最近才开始学习状压dp,以前就听听没写过。到目前为止,我对状压dp的应用就是用数位表示点,通过枚举不同的数,也就是不同的到达状态,来实现状态的转移。跟着网上的博客切题中

1.POJ3254 Corn Fields
http://poj.org/problem?id=3254

做的第一个状压题,基本是跟着网上的思路写的,状压dp真是很神奇,通过位运算能解决很多问题。

#include<cstdio>
#include<cstring>
using namespace std;
int ok[1<<13], each[1<<13], cnt;
bool check(int x)
{
    return (x&(x<<1));
}
bool check2(int x, int y)
{
    return (ok[x]&each[y]);
}
int main()
{
    int m, n, a, dp[13][1<<13];
    while(~scanf("%d%d", &m, &n))
    {
        memset(dp, 0, sizeof(dp));
        memset(each, 0, sizeof(each));
        memset(ok, 0, sizeof(ok));
        cnt=0;
        for(int i=1; i<=m; i++)
        {
            for(int j=1; j<=n; j++)
            {
                scanf("%d", &a);
                if(a==0)
                {
                     each[i]+=(1<<(j-1));
                }
            }
      //      printf("each[%d]=%d\n", i, each[i]);
        }
        for(int i=0; i<(1<<n); i++)
        {
            if(!check(i))
                ok[cnt++]=i;
        }
        for(int i=0; i<cnt; i++)
        {
            if(!check2(i, 1))
            {
                 dp[1][i]=1;
            }
        }
        for(int i=2; i<=m; i++)
        {
            for(int j=0; j<cnt; j++)//第i行当前处于j种取值
            {
                if(check2(j, i))//如果第j种取值不符合规定
                    continue;
                for(int h=0; h<cnt; h++) //第i-1行当前处于h种取值
                {
                    if(check2(h, i-1))
                        continue;
                    if(!(ok[j]&ok[h]))
                        dp[i][j]+=dp[i-1][h];
                }
            }
        }
        int ans=0;
        for(int i=0; i<cnt; i++)
        {
            ans+=dp[m][i];
            ans%=100000000;
        }
        printf("%d\n", ans);
    }
    return 0;
}

2.hdu 3001 Travelling

http://acm.hdu.edu.cn/showproblem.php?pid=3001

因为是最多经过两次,所以用三进制表示,因为习惯了二进制表示,一开始写的很丑而且都调不出bug。除了三进制之外,思路其实差不多。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
#define INF 0x1f1f1f1f
int dis[15][15];
int index[15] ;
int record[60000][15];
int dp[60000][15];
void init_record()
{
    for(int i=0; i<59050; i++)
    {
       int t = i;
       for(int j = 1; j <= 10; ++j){
           record[i][j] = t%3;
           t /= 3;
           if(t == 0)break;
       }
    }
    index[0]=0;
    index[1]=1;
    for(int i=2; i<=11; i++)
   {
         index[i]=index[i-1]*3;
   }
}

int main()
{
    int n, m, a, b, w;
    init_record();
    while(~scanf("%d%d", &n, &m))
    {
       memset(dis, INF, sizeof(dis));
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d", &a, &b, &w);
            if(w<dis[a][b])
                dis[a][b]=dis[b][a]=w;
        }
        memset(dp, INF, sizeof(dp));
        int ans=INF;
        for(int state=1 ; state<index[n+1]; state++)
        {
            int flag=1;
            for(int i=1; i<=n; i++)
            {
                if(record[state][i]==0)
                {
                    flag=0;continue;
                }
                if(state==index[i])
                {
                    dp[state][i]=0;
                    continue;
                }
                for(int j=1; j<=n; j++)
                {
                    if(i==j) continue;
                    if(record[state][j]==0||dis[j][i]==INF) continue;
                    int newstate=state-index[i];
                    dp[state][i]=min(dp[state][i], dp[newstate][j]+dis[j][i]);
                }
            }
            if(flag)
            {
                for(int j=1; j<=n; j++)
                    ans=min(ans, dp[state][j]);
            }
        }
        if(ans==INF)
            printf("-1\n");
        else
            printf("%d\n", ans);
    }
    return 0;
}

。。。待续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值