题意:
你从学校回到家要T的时间,你现在有n首歌,每首歌的播放时间为ti,编号为gi,你现在想要确定播放一些歌使得你正好用T分钟听完这些歌,且每次连续播放的两首歌编号不同。问你有多少种播放方法,注意顺序不同视为两种方法。
题解:
n只有15的大小,T是225,g是3.正好是一个状压的时间复杂度,那么用
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]表示第i分钟,状态为j的时候,最后一首歌编号为k的情况数。(j是用二进制表示每首歌的状态)
那么状态转移方程就是
( d p [ i ] [ k ∣ ( 1 < < ( j − 1 ) ) ] [ g [ j ] ] + = d p [ i − t [ j ] ] [ k ] [ l ] ) % = m o d (dp[i][k|(1<<(j-1))][g[j]]+=dp[i-t[j]][k][l])\%=mod (dp[i][k∣(1<<(j−1))][g[j]]+=dp[i−t[j]][k][l])%=mod
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1e9+7;
const int N=(1<<15);
ll dp[226][N][4];
int t[20],g[20];
int main()
{
int n,T;
scanf("%d%d",&n,&T);
for(int i=1;i<=n;i++)
scanf("%d%d",&t[i],&g[i]);
memset(dp,-1,sizeof(dp));
dp[0][0][0]=1;
for(int i=1;i<=T;i++)
{
for(int j=1;j<=n;j++)
{
if(t[j]>i)
continue;
for(int k=0;k<(1<<n);k++)
{
for(int l=0;l<=3;l++)
{
if(l!=g[j]&&((k&(1<<(j-1)))==0)&&~dp[i-t[j]][k][l])
{
if(dp[i][k|(1<<(j-1))][g[j]]==-1)
dp[i][k|(1<<(j-1))][g[j]]=dp[i-t[j]][k][l];
else
(dp[i][k|(1<<(j-1))][g[j]]+=dp[i-t[j]][k][l])%=mod;
}
}
}
}
}
ll ans=0;
for(int i=0;i<(1<<n);i++)
for(int j=1;j<=3;j++)
if(~dp[T][i][j])
ans=(ans+dp[T][i][j])%mod;
return 0*printf("%lld\n",ans);
}