Haiku(状压dp)
题目描述
对于一个长度问n的数组A,设第i个元素为Ai(0≤i<n)。
问有多少个长度为n的数组A,满足1≤Ai≤10,且这个数组是诗。
给定x,y,z,一个长度为n的数组是诗当且仅当存在i,j,k,l(0≤i<j<k<l≤N)使得:
ai+ai+1+...+aj−1=x。
aj+aj+1+...+ak−1=y。
ak+ak+1+...+al−1=z。
输入
一行,四个整数,分别表示n x y z
输出
输出满足条件的数组数量 mod 109+7。
样例输入
3 5 7 5
样例输出
1
hint
思路
逆向思考,总数减去不满足条件的数量。
既然 x+y+z<=17, 则可用二进制表示和为多大,比如:100,可表示3, 1000100可表示6、3、4。
因为,可以在O(2^17)枚举各种状态的次数,总数减去状态和即为答案。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxm = 1<<17;
const int mod = 1e9+7;
typedef long long ll;
inline int bit(int x, int mov) { return x|(1<<mov-1); }
int n, m, x, y, z;
int vp=0;
ll ans = 1, dp[42][maxm];
int main() {
scanf("%d%d%d%d", &n, &x, &y, &z);
m = 1<<(x+y+z); --m;
vp = bit(vp, x+y+z), vp = bit(vp, y+z), vp = bit(vp, z);
dp[0][0] = 1;
for (int i=1; i<=n; ++i) {
(ans *= 10) %= mod;
for (int mask=0; mask<=m; ++mask) {
if(!dp[i-1][mask]) continue;
for (int k=1; k<=10; ++k) {
int cur = bit(mask<<k, k);
cur &= m;
if((cur&vp)!=vp)
(dp[i][cur] += dp[i-1][mask]) %= mod;
}
}
}
for (int mask=0; mask<=m; ++mask)
ans = (ans - dp[n][mask] + mod) % mod;
printf("%lld\n", ans);
return 0;
}