小杨有一个正整数 n n n,小杨想将它拆分成若干完全平方数的和,同时小杨希望拆分的数量越少越好。
编程计算总和为 n n n 的完全平方数的最小数量。
输入只有一行一个正整数 n n n。
输出一行一个整数表示答案。
18
2
对全部的测试数据,保证 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105。
这道题是我之前讲过的DP,相信不难看出吧,六级考的都是简单一维DP,这里用 f ( i ) f(i) f(i) 表示总和为 i i i 的完全平方数的最小数量,转移不难,直接放代码,具体看:
#include
using namespace std;
int n;
int f[100100];
int main() {
scanf("%d", &n);
memset(f, 0x3f, sizeof(f));
f[1] = 1, f[0] = 0;
for (int i = 2; i <= n; i++) {
for (int j = sqrt(i); i - int(j * j) <= i && j > 0; j--) {
f[i] = min(f[i], f[i - int(j * j)] + 1);
}
}
printf("%d", f[n]);
return 0;
}
树的知识稍后扩展,暂不做讲解。
小杨有一个有一个 n × m n \times m n×m 的矩阵,仅包含 01?
三种字符。矩阵的行从上到下编号依次为 1 , 2 , … , n 1,2,\dots, n 1,2,…,n,列从左到右编号依次为 1 , 2 , … , m 1, 2, \dots, m 1,2,…,m。小杨开始在矩阵的左上角 ( 1 , 1 ) (1,1) (1,1),小杨只能向下或者向右移动,最终到达右下角 ( n , m ) (n, m) (n,m) 时停止,在移动的过程中每经过一个字符 1
得分会增加一分(包括起点和终点),经过其它字符则分数不变。小杨
的初始分数为 0 0 0 分。
小杨可以将矩阵中不超过 k k k 个字符 ?
变为字符 1
。小杨在修改矩阵后,会以最优的策略从左上角移动到右下角。他想
知道自己最多能获得多少分。
第一行包含一个正整数 t t t,代表测试用例组数,接下来是 t t t 组测试用例。对于每组测试用例,一共 n + 1 n + 1 n+1 行。
第一行包含三个正整数 n , m , x n, m, x n,m,x,含义如题面所示。
之后 n n n 行,每行一个长度为 m m m 的仅含 01x?
的字符串。
对于每组测试用例,输出一行一个整数,代表最优策略下小杨的得分最多是多少。
2
3 3 1
000
111
01?
3 3 1
000
?0?
01?
4
2
对于第二组测试用例,将 ( 1 , 1 ) (1,1) (1,1) 或者 ( 3 , 3 ) (3,3) (3,3) 变为 1 1 1 均是最优策略。
子任务编号 | 数据点占比 | t t t | n , m n,m n,m | x x x |
---|---|---|---|---|
1 1 1 | 30 % 30\% 30% | ≤ 5 \leq 5 ≤5 | ≤ 10 \le 10 ≤10 | = 1 =1 =1 |
2 2 2 | 30 % 30\% 30% | ≤ 10 \le 10 ≤10 | ≤ 500 \le 500 ≤500 | ≤ 30 \le 30 ≤30 |
3 3 3 | 40 % 40\% 40% | ≤ 10 \le 10 ≤10 | ≤ 500 \le 500 ≤500 | ≤ 300 \le 300 ≤300 |
对全部的测试数据,保证 1 ≤ t ≤ 10 1 \leq t \leq 10 1≤t≤10, 1 ≤ n , m ≤ 500 1 \leq n,m \leq 500 1≤n,m≤500, 1 ≤ x ≤ 300 1 \leq x \leq 300 1≤x≤300,保证所有测试用例 n × m n \times m n×m 的总和不超过 2.5 × 1 0 5 2.5 \times 10^5 2.5×105。
可以看,算比较简单的DP吧,很暴力, f ( i , j , k ) f(i,j,k) f(i,j,k),多一维处理 ? ? ?。代码:
#include
using namespace std;
char a[510][510];
int f[510][510][310];
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i <= n;i++){
for(int j=1;j <= m;j++){
cin>>a[i][j];
}
}
memset(f,0,sizeof(f));
for(int i=1;i <= n;i++){
for(int j=1;j <= m;j++){
for(int x=0;x <= k;x++){
if(a[i][j]!='?'){
f[i][j][x]=max(f[i-1][j][x],f[i][j-1][x])+1;
if(a[i][j]=='0') f[i][j][x]--;
}else{
f[i][j][x]=max(f[i-1][j][x],f[i][j-1][x]);
if(x!=0)f[i][j][x]=max(f[i][j][x],max(f[i-1][j][x-1],f[i][j-1][x-1])+1);
}
}
}
}
int ans=0;
for(int i=0;i <= k;i++){
ans=max(ans,f[n][m][i]);
}
printf("%d\n",ans);
}
return 0;
}
T2后面图论讲,会在12月7日前。
小杨有 n n n 对不同的手套,每对手套由左右各一只组成。
小杨想知道从中取出 m m m 只手套,恰好包含 k k k 对手套的情况有多少种。
小杨认为两种取出的情况不同,当且仅当两种情况取出的手套中存在不同的手套(同一对手套的左右手也视为不同的手套)。
本题单个测试点内由多组测试数据。第一行是一个整数 t t t,表示测试用例数量。接下来是 t t t 组测试用例,每组一行。
每组数据只有一行三个正整数 n , m , k n,m,k n,m,k,表示手套数量、取出的手套数和目标对数。
对每组数据,输出一行一个整数表示答案对 1 0 9 + 7 10^9 + 7 109+7 取模的结果。
2
5 6 2
5 1 5
120
0
子任务 | 占比 | t t t | n n n | m m m | k k k |
---|---|---|---|---|---|
1 1 1 | 30 % 30\% 30% | ≤ 5 \leq 5 ≤5 | 1000 1000 1000 | ≤ 3 \le 3 ≤3 | = 1 =1 =1 |
2 2 2 | 30 % 30\% 30% | ≤ 5 \leq 5 ≤5 | ≤ 5 \leq 5 ≤5 | ≤ 10 \leq 10 ≤10 | ≤ 5 \leq 5 ≤5 |
3 3 3 | 40 % 40\% 40% | 1 0 5 10^5 105 | 1000 1000 1000 | 2000 2000 2000 | 2000 2000 2000 |
对全部的测试数据,保证 1 ≤ t ≤ 1 0 5 1 \leq t \leq 10^5 1≤t≤105, 1 ≤ n ≤ 1000 1 \leq n \leq 1000 1≤n≤1000, 1 ≤ m ≤ 2 × n 1 \leq m \leq 2 \times n 1≤m≤2×n, 1 ≤ k ≤ n 1 \le k \le n 1≤k≤n。
∵ \because ∵ 恰好包含 k k k 对手套的情况。 ∴ \therefore ∴ C n k C_n^k Cnk,则答案为 C n k ⋅ C 2 ( n − k ) m − 2 k C_n^k·C_{2(n-k)}^{m-2k} Cnk⋅C2(n−k)m−2k,不作详细代码。
树上&图上DP下章讲!