题目链接: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);
}
}