题面
题意
现在你有 n n n首歌,第 i i i首歌的播放时间为 t i t_i ti,种类为 f i f_i fi,其中 1 ≤ f i ≤ 3 1 \leq f_i \leq 3 1≤fi≤3,现在你从家到学校需要花费 T T T的时间,你在路上不想闲着,现在你要选几首歌按照一定的顺序播放,你要保证着几首歌的时间总和为 T T T(每首歌只会播放一次),并且在播放的时候不会连续播放同一种类的歌曲,计算共有多少种方案,答案对 1 0 9 + 7 10^9+7 109+7取模。
思路
状压
d
p
dp
dp,
d
p
i
,
j
dp_{i,j}
dpi,j代表第
i
i
i中状态最后播放的一首歌为种类
j
j
j的方案数,那么状态转移就有:
d
p
i
(
1
<
<
k
)
,
t
(
t
!
=
j
)
=
d
p
i
(
1
<
<
k
)
,
t
(
t
!
=
j
)
+
d
p
i
,
j
dp_{i(1<<k),t(t!=j)}=dp_{i(1<<k),t(t!=j)}+dp_{i,j}
dpi(1<<k),t(t!=j)=dpi(1<<k),t(t!=j)+dpi,j。
处理完所有状态的方案数之后,再利用二进制枚举,判断该状态是否可行,即总时间是否等于
T
T
T。
时间复杂度
n
∗
(
2
n
)
n*(2^n)
n∗(2n)。
code
#include<bits/stdc++.h>
using namespace std;
long long dp[(1<<17)+20][4];//第i个状态最后一个为颜色j的方案数
const int mod = 1e9+7;
int cal(int n){
int ans=0;
while(n){
ans+=(n&1);
n>>=1;
}
return ans;
}
int main(){
int a[20],b[20];
int n,T;
cin>>n>>T;
for(int i=0;i<n;i++){
cin>>a[i]>>b[i];
}
for(int i=0;i<(1<<n);i++){
if(cal(i)==1){
for(int j=0;j<n;j++){
if(i&(1<<j)){
dp[i][b[j]]=1;
}
}
}
}
for(int i=0;i<(1<<n);i++){
for(int k=0;k<n;k++){
if(!(i&(1<<k))){//下一个颜色
if(b[k]==1) dp[i|(1<<k)][b[k]]=(dp[i|(1<<k)][b[k]]%mod+dp[i][2]%mod+dp[i][3]%mod)%mod;
if(b[k]==2) dp[i|(1<<k)][b[k]]=(dp[i|(1<<k)][b[k]]%mod+dp[i][1]%mod+dp[i][3]%mod)%mod;
if(b[k]==3) dp[i|(1<<k)][b[k]]=(dp[i|(1<<k)][b[k]]%mod+dp[i][1]%mod+dp[i][2]%mod)%mod;
}
}
}
long long ans=0;
for(int i=0;i<(1<<n);i++){
int ss=0;
for(int j=0;j<n;j++){
if(i&(1<<j)) ss+=a[j];
}
if(ss==T){
ans=(ans%mod+dp[i][1]%mod+dp[i][2]%mod+dp[i][3]%mod)%mod;
}
}
cout<<ans<<endl;
return 0;
}