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
", ans);
   return 0;
}
原文地址:https://www.cnblogs.com/acerkoo/p/10928086.html