[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 }

 

posted @ 2019-09-15 21:00  祈梦生  阅读(406)  评论(0编辑  收藏  举报