题意:给定n和s,求f(i,j,k,l,m)表示下标i和j的数必选,k和l不选且选出数的和为s的选法方案数。
思路:dp[i][j][k][l],表示前i个物品,权值和为j,已有k个必选,l个必不选的方案数。
转移:枚举i
1.选,增加权值,增加必选个数。
2.选,增加权值,不增加必选个数。
3.不选,不增加权值,增加不必选个数。
4.不选,不增加权值,不增加必选个数。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3+5;
const int mod = 1e9+7;
int dp[maxn][maxn][3][3];
int a[maxn];
int main(void)
{
int _, n, s;
cin >> _;
while(_--)
{
memset(dp, 0, sizeof(dp));
scanf("%d%d", &n, &s);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
dp[1][a[1]][0][0] = 1;
dp[1][a[1]][1][0] = 1;
dp[1][0][0][0] = 1;
dp[1][0][0][1] = 1;
for(int i = 2; i <= n; i++)
{
for(int j = 0; j <= s; j++)
{
int tmp = j+a[i];
//选
if(tmp <= s)
{
for(int k = 0; k <= 2; k++)
{
dp[i][tmp][0][k] = (dp[i][tmp][0][k]+dp[i-1][j][0][k])%mod;
dp[i][tmp][1][k] = (dp[i][tmp][1][k]+dp[i-1][j][0][k])%mod;
dp[i][tmp][1][k] = (dp[i][tmp][1][k]+dp[i-1][j][1][k])%mod;
dp[i][tmp][2][k] = (dp[i][tmp][2][k]+dp[i-1][j][1][k])%mod;
dp[i][tmp][2][k] = (dp[i][tmp][2][k]+dp[i-1][j][2][k])%mod;
}
}
//不选
for(int k = 0; k <= 2; k++)
{
dp[i][j][k][0] = (dp[i][j][k][0]+dp[i-1][j][k][0])%mod;
dp[i][j][k][1] = (dp[i][j][k][1]+dp[i-1][j][k][0])%mod;
dp[i][j][k][1] = (dp[i][j][k][1]+dp[i-1][j][k][1])%mod;
dp[i][j][k][2] = (dp[i][j][k][2]+dp[i-1][j][k][1])%mod;
dp[i][j][k][2] = (dp[i][j][k][2]+dp[i-1][j][k][2])%mod;
}
}
}
ll ans = 0;
for(int i = 1; i <= s; i++)
ans = (ans+dp[n][i][2][2])%mod;
printf("%lld\n", ans*4%mod);
}
return 0;
}