用完全背包解决求极大无关组个数问题
原题:https://www.acwing.com/problem/content/534/
1 532. 货币系统
2
3
4 在网友的国度中共有 n 种不同面额的货币,第 i 种货币的面额为 a[i],你可以假设每一种货币都有无穷多张。
5
6 为了方便,我们把货币种数为 n、面额数组为 a[1..n] 的货币系统记作 (n,a)。
7
8 在一个完善的货币系统中,每一个非负整数的金额 x 都应该可以被表示出,即对每一个非负整数 x,都存在 n 个非负整数 t[i] 满足 a[i]×t[i] 的和为 x。
9
10 然而,在网友的国度中,货币系统可能是不完善的,即可能存在金额 x 不能被该货币系统表示出。
11
12 例如在货币系统 n=3, a=[2,5,9] 中,金额 1,3 就无法被表示出来。
13
14 两个货币系统 (n,a) 和 (m,b) 是等价的,当且仅当对于任意非负整数 x,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。
15
16 现在网友们打算简化一下货币系统。
17
18 他们希望找到一个货币系统 (m,b),满足 (m,b) 与原来的货币系统 (n,a) 等价,且 m 尽可能的小。
19
20 他们希望你来协助完成这个艰巨的任务:找到最小的 m。
21
22 输入格式
23 输入文件的第一行包含一个整数 T,表示数据的组数。
24
25 接下来按照如下格式分别给出 T 组数据。
26
27 每组数据的第一行包含一个正整数 n。
28
29 接下来一行包含 n 个由空格隔开的正整数 a[i]。
30
31 输出格式
32 输出文件共有 T 行,对于每组数据,输出一行一个正整数,表示所有与 (n,a) 等价的货币系统 (m,b) 中,最小的 m。
33
34 数据范围
35 1≤n≤100,
36 1≤a[i]≤25000,
37 1≤T≤20
38 输入样例:
39 2
40 4
41 3 19 10 6
42 5
43 11 29 13 19 17
44 输出样例:
45 2
46 5
理解题意,前面大段的是无关内容,最重要的是:
很明显了,这不就是线性代数中的求极大无关组个数吗?
即:a数组与b数组之间可以相互表示,b是a的极大无关组
即要在a数组中找到不被数组中任何元素表示出的元素
由于这里系数只能为非负数,所以,一个元素只能被其小于的元素表示
所以我们先排一个序(从小到大),问题就变成了
由 对于a[i]是否能够由a[1],a[2].....a[i-1](无限个)组成,即是否 a[i]=k1*a[1]+k2*a[2]+...ki-1*a[i-1](其中k不全为0);
如果这样写会超时:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 110;
int t;
int main()
{
cin >> t;
while (t--)
{
int a[N], n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
sort(a + 1, a + n + 1);
int ans = 0;
for (int i = 1; i <= n; i++) //判断这数是否要选
{
//超时原因在这里,我们可以发现我们浪费了很多已经算过的
//每次还有再算一遍
bool dp[25010];
memset(dp, false, sizeof(dp));
dp[0] = true;
for (int j = 1; j <= i - 1; j++)
{
for (int k = a[j]; k <= a[i]; k++)
{
dp[k] |= dp[k - a[j]];
}
}
if (!dp[a[i]])
ans++;
}
cout << ans << endl;
}
return 0;
}
其实for(int j=1;j<=i-1;j++)这个for循环可以省略,
写成:
1 #include <iostream>
2 #include <algorithm>
3 #include <cstring>
4 using namespace std;
5 const int N = 110;
6 int t;
7 int main()
8 {
9 cin >> t;
10 while (t--)
11 {
12 int a[N], n;
13 cin >> n;
14 for (int i = 1; i <= n; i++)
15 cin >> a[i];
16 sort(a + 1, a + n + 1);
17 int ans = 0;
18 bool dp[25010];
19 memset(dp,false,sizeof(dp));
20 dp[0]=true;
21 for (int i = 1; i <= n; i++)
22 {
23 if (dp[a[i]])
24 continue;
25 ans++;
26 for (int j = a[i]; j <= a[n]; j++)
27 {
28 dp[j] |= dp[j - a[i]];
29 }
30 }
31 cout << ans << endl;
32 }
33 return 0;
34 }