POJ 2441

题目链接:http://poj.org/problem?id=2441

题意:有头牛,m个场地,每个牛有各自喜好的场地,把这些牛安排到场地中去,每头牛都要安放在它喜好的场地,有多少种安排方法

题目类型:状压dp

状态转移方程:定义dp[n]1<<m],dp[i[][j]表示前i头牛在j的安排状态下,有多少种方法

注意:本题要用到滚动数组

代码:

#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;

const int M=1<<21;

int map[25][25];
int dp[2][M];

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        int p;
        memset(map,0,sizeof(map));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&p);
            for(int j=1;j<=p;j++)
            {
                int t;
                scanf("%d",&t);
                map[i][t]=1;
            }
        }
        memset(dp,0,sizeof(dp));
        for(int i=0;i<=m;i++)
            if(map[1][i])
            {
                dp[1][1<<i]++;
            }
        for(int i=2;i<=n;i++)
        {    for(int j=1;j<=m;j++)
            {
                if(map[i][j])
                {
                    for(int s=0;s<1<<(m+1);s++)
                    {
                        if(dp[(i-1)%2][s])
                            if((s&(1<<j))==0)
                            {

                                dp[i%2][s|1<<j]+=dp[(i-1)%2][s];
                                
                            }   
                    }
                }
            }
            memset(dp+(i+1)%2,0,sizeof(dp)/2);
        }
            int ans=0;
            for(int s=0;s<(1<<m+1);s++)
                ans+=dp[n%2][s];
            printf("%d\n",ans);
    }
}
其实本题可以不用滚动数组,对于dp[i][s|1<<j],所需状态为dp[i-1][s],肯定在dp[i][s|i<<j]之前,所以从后往前枚举s即不会覆盖所需状态。

代码:

#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;

const int M=1<<21;

int map[25][25];
int dp[M];

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        int p;
        memset(map,0,sizeof(map));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&p);
            for(int j=1;j<=p;j++)
            {
                int t;
                scanf("%d",&t);
                map[i][t]=1;
            }
        }
        memset(dp,0,sizeof(dp));
        dp[0]=1;
        for(int i=1;i<=n;i++)
        {    
            for(int s=((1<<m+1)-1);s>=0;s--)
            {
                if(dp[s])
                {
                    for(int j=1;j<=m;j++)
                    {
                        if(map[i][j])
                        {
                            if((s&(1<<j))==0)
                            {

                                dp[s|(1<<j)]+=dp[s];
                            } 
                        }
                    }
                }
                dp[s]=0;
            }
        }
            int ans=0;
            for(int s=0;s<(1<<m+1);s++)
                ans+=dp[s];
            printf("%d\n",ans);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值