Skill Up
编号:0011
试题来源:AtCoder
试题描述
怪人想要学习 M M M门算法,假设对于技艺的掌握程度是可以量化的,怪人的期望是对于这 M M M门算法,每一门的掌握程度都要大于 X X X。提高掌握程度的方法是读书,假设书店中共有 N N N本书,其中第 i i i本书的价格是 C i C_i Ci,对于第 j j j门算法的提升程度是 A i , j A_{i,j} Ai,j。以上均为已知,判断能否满足怪人的期望,如果可以的话,请问最少花费?
取值范围:
- 所有值都是整数
- 1 ≤ N , M ≤ 12 1\leq N, M\leq 12 1≤N,M≤12
- 1 ≤ X ≤ 1 0 5 1\leq X \leq 10^5 1≤X≤105
- 1 ≤ C i ≤ 1 0 5 1\leq C_i \leq 10^5 1≤Ci≤105
- 0 ≤ A i , j ≤ 1 0 5 0\leq A_{i,j}\leq 10^5 0≤Ai,j≤105
输入格式
N M X
C 1 C_1 C1 A 1 , 1 A_{1,1} A1,1 A 1 , 2 A_{1,2} A1,2 … \dotso … A 1 , M A_{1,M} A1,M
⋮ \vdots ⋮
C N C_N CN A N , 1 A_{N,1} AN,1 A N , 2 A_{N,2} AN,2 ⋯ \cdots ⋯ A N , M A_{N,M} AN,M
解答算法
算法思路
从头开始遍历,对于每本书都有买和不买两种可能,将 2 N 2^N 2N中可能全部遍历一遍,取其中的满足条件的最小价格,如果没有能够满足条件的,就输出-1
遍历过程利用的是func
函数,两个参数nn
和sum
分别表示当前已经遍历到第几本书,和当前遍历过的价格和,对于第i
本书,只有买和不买两种可能,因此func(nn,sum)
继续递归调用只会有两种情况,买func(nn+1,sum+c[nn])
,不买func(nn+1,sum)
,注意在买的时候,同时要改变我们要提升的技能的点数。
代码实现
#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<algorithm>
#include<functional>
#define MM 2000000000
void func(int, int);
int n, m, x, c[12], a[12][12], wk[12], mn;//n、m、x、c、a都是题目意思,wk表示m门技能的掌握程度,mn表示最后的付款
using namespace std;
int main(void)
{
int i, j;
scanf("%d %d %d", &n, &m, &x);
for (i = 0; i < n; i++) {
scanf("%d", &c[i]);
for (j = 0; j < m; j++) {
scanf("%d", &a[i][j]);
}
}
mn = MM; //初始给mn赋一个极大值,如果最后还是这个极大值,说明没有满足条件的解,输出-1就好
for (i = 0; i < m; i++) wk[i] = 0; //把掌握程度置0
func(0, 0); //开始调用
if (mn != MM) printf("%d\n", mn);
else printf("-1\n");
return 0;
}
void func(int nn, int sum) //把每一种情况都遍历了一遍
{
int i, flg;
if (nn == n) { //递归到尽头
flg = 1;
for (i = 0; i < m; i++) { //判断是否满足掌握程度大于X
if (wk[i] < x) {
flg = 0; break;
}
}
if (flg == 1) mn = min(mn, sum); //满足的话,更新mn
}
else {
func(nn + 1, sum); //不买第nn本书
for (i = 0; i < m; i++) {
wk[i] += a[nn][i];
}
func(nn + 1, sum + c[nn]); //买第nn本书
for (i = 0; i < m; i++) {
wk[i] -= a[nn][i];
}
}
}
复杂度分析
- 时间复杂度:整个过程中对所有的 2 n 2^n 2n中情况均遍历了一遍,因此时间复杂度 O ( 2 n ) O(2^n) O(2n)
- 空间复杂度:主要是递归调用的栈空间,栈空间深度为 n n n,因此空间复杂度 O ( n ) O(n) O(n)