状压dp 。
学习的时候唯一的疑问就是不知道怎么转换状态 。还是因为对状态压缩不是多么懂
刷了这个题就好多了 。
把能完成 第i 个任务的状态进行记录 。
之后用这个状态进行dp
最好用的就是二进制 。
1代表选过 0代表没选过 。
在这个上面进行状态压缩 。
代码注释下看
#include <bits/stdc++.h>
using namespace std;
#define mo 205
#define pus push_back
int a[mo][mo],b[mo][mo];
int tag[mo];
int dp[mo][3000];
vector<int > d[mo];
int main()
{
int t;
cin>>t;
int l=1;
while(t--)
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
d[i].clear();
cin>>a[i][0];
for(int j=1;j<=a[i][0];j++)
cin>>a[i][j];
}
for(int i=0;i<m;i++)
{
cin>>b[i][0];
for(int j=1;j<=b[i][0];j++)
cin>>b[i][j];
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<(1<<m);j++)
{
memset(tag,0,sizeof(tag));
for(int k=0;k<m;k++)
{
if(j&(1<<k)) //此代表k是否属于状态j 。
{
for(int r=1;r<=b[k][0];r++)
{
tag[b[k][r]]=1;
}
}
}
int flag=1;
for(int j=1;j<=a[i][0];j++)//如果这个状态可以完成i任务的话
{
if(tag[a[i][j]]==0)
{
flag=0;
break;
}
}
// if(flag)cout<<j<<endl;
if(flag) d[i].pus(j);//将这个状态保存
}
}
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
for(int j=0;j<(1<<m);j++)
{
for(int k=0;k<d[i].size();k++)
{
int x=d[i][k];
if((x|j)==j)//如果x状态属于j 的话 则代表 j-x的状态可以进行dp。和背包一样
{
dp[i][j]=max(dp[i][j],dp[i-1][j-x]+1);
}
//cout<<dp[i][j]<<' ';
}
// cout<<endl;
dp[i][j]=max(dp[i][j],dp[i-1][j]);
}
}
cout<<"Case #"<<l++<<": "<<dp[n][(1<<m)-1]<<endl;
}
}