[2019上海网络赛J题]Stone game
题目链接 CSLnb!
题意是求出给定集合中有多少个合法子集,合法子集的定义为,子集和>=总和-子集和$\& \&$子集和-(子集的子集和)<=总和-子集和。
其实就是很简单的dp,先将集合从大到小排序,dp[i][j]表示以a[i]为子集的最小值时,子集和为j的方案数。因为排序后保证遍历到的a[i]一定为当前最小值,所以暴力统计转移即可。
最后在统计一遍合法答案。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int mod = 1e9 + 7; 5 int a[310]; 6 int dp[305][150010]; 7 int sm[150010]; 8 int add(int a, int b) { 9 a += b; 10 if (a >= mod) 11 a -= mod; 12 return a; 13 } 14 int main() { 15 int t; 16 scanf("%d", &t); 17 while (t--) { 18 int n, sum = 0; 19 scanf("%d", &n); 20 for (int i = 1; i <= n; i++) 21 scanf("%d", &a[i]), sum += a[i]; 22 sort(a + 1, a + 1 + n, greater<int>()); 23 dp[0][0] = 1; 24 sm[0] = 1; 25 for (int i = 1; i <= n; i++) { 26 for (int j = sum; j >= a[i]; j--) { 27 dp[i][j] = sm[j - a[i]]; 28 sm[j] = add(sm[j], dp[i][j]); 29 } 30 } 31 int ans = 0; 32 for (int i = 1; i <= n; i++) { 33 for (int j = 0; j <= sum; j++) { 34 if (j >= sum - j && j - a[i] <= sum - j) 35 ans = add(ans, dp[i][j]); 36 dp[i][j] = 0; 37 sm[j] = 0; 38 } 39 } 40 printf("%d\n", ans); 41 } 42 }