思路:
可以发现(n)很小,考虑状压(dp)
(dp[i][j])表示只从前(i)个中选,使得集合(j)中的箱子都被打开花费的最小代价。
初始化:
初始化为正无穷并且(dp[0][0]=0)
转移:
不选第(i)个,从(dp[i-1][j])转移过来
选第(i)个,转移到(dp[i][j|b[i]]),上一个状态为(dp[i-1][j])
答案:
[res=dp[m][(1<<n)-1]
]
细节:
为了方便状压,箱子的编号最好从(0)开始,将输入编号全部(-1)
复杂度:
[O(m*2^{n})
]
代码:
const int maxn=13;
ll n,m,a[1100],b[1100];
ll dp[1100][(1<<maxn)+10];
int main()
{
n=read,m=read;
rep(i,1,m){
a[i]=read;
ll t=read,x=0;
rep(j,1,t){
ll tmp=read;
x|=(1<<(tmp-1));///-1是为了使得状压枚举的时候更好枚举
}
b[i]=x;
}
memset(dp,0x3f,sizeof dp);
dp[0][0]=0;
for(int i=1;i<=m;i++){
for(int j=0;j<(1<<n);j++){
dp[i][j]=min(dp[i][j],dp[i-1][j]);///不选第i个
dp[i][j|b[i]]=min(dp[i][j|b[i]],dp[i-1][j]+a[i]);
}
}
if(dp[m][(1<<n)-1]>inf/2) puts("-1");
else cout<<dp[m][(1<<n)-1]<<endl;
return 0;
}