这几天专门挑些简单的算法题目做做,怕忘记,贴在这里。
JOJ 2171 An Easy Problem: http://acm.jlu.edu.cn/joj/showproblem.php?pid=2171
给定一个正整数 N , N 不超过 40 ,问在 N 位由数字 0 、 1 组成的所有串中,没有连续 3 个 1 出现的串的个数是多少。比如 N 为 1 的时候,有 0 、 1 两个, N 为 2 的时候,有 00 、 01 、 10 、 11 四个, N 为 3 的时候, 111 就不合法了,此时这样串有 7 个。
解法:动态规划。所有的串都能按照最末两位分成四组。假设在 n 位合法的 0 、 1 串中,以 00 结尾的串的个数为 f0(n) ,以 01 结尾的串个数为 f1(n) ,以 10 结尾的串个数为 f2(n) ,以 11 结尾的串个数为 f3(n) ,则 n 位合法的 0 、 1 串的总数 f(n)=f0(n)+f1(n)+f2(n)+f3(n) 。当串的长度扩展到 n+1 位时,我们看这它们各自的个数如何动态变化。从 n 位到 n+1 位,只需要在 n 位串的末尾追加一位 0 或一位 1 即可。对四个函数分类讨论:
以 00 结尾:加 0 后变成以 00 结尾,加 1 后变成 01 结尾;
以 01 结尾:加 0 后变成以 10 结尾,加 1 后变成 11 结尾;
以 10 结尾:加 0 后变成以 00 结尾,加 1 后变成 01 结尾;
以 11 结尾:加 0 后变成以 10 结尾,加 1 后不合法。
所以,有: f0(n+1) = f0(n) + f2(n), f1(n+1) = f0(n) + f2(n), f2(n+1) = f1(n) + f3(n), f3(n+1) = f1(n) 。
当 n 为 2 时,有 f0(2) = f1(2) = f2(2) = f3(2) = 1 。这样,经过递推可以很快计算出结果。中间要考虑溢出的情况。当 n = 37 时,结果超过了 70 亿,必须用 double 类型。
JOJ 2488 Function Value: http://acm.jlu.edu.cn/joj/showproblem.php?pid=2488
函数 f(n) 如下定义:当 n=1 时, f(n) = 1 ;当 n 是一个素数时, f(n) = f(n – 1) ;否则, f(n) = f(k) + 1 ,其中 k 是 n 除自身以外最大的因子。输入数据 n 为不超过 2 的 31 次方的正整数。
这个题目直接按题意来做即可。因为 n 是素数时 n-1 ,这就变成了合数,然后再除以 k , k 至少是 2 ,所以 n 最起码被砍到了一半。所以这个题目的计算量就是 O(log(n)) 的。代码如下:
#include <stdio.h>
int main() {
unsigned int n, ans, k;
while ( scanf ( "%u" , &n) != EOF) {
ans = 0;
while (1) {
if (n == 1 || n == 2) { ans++; break ; }
for (k = 2; k * k <= n; k++)
if (n % k == 0) break ;
if (n % k) n--;
else { n /= k; ans++; }
}
printf ( "%u/n" , ans);
}
return 0;
}
JOJ 1868 Blocks: http://acm.jlu.edu.cn/joj/showproblem.php?pid=1868
有 N 个长宽高都为 1 的小立方体,把它们砌成一个长方体或正方体,能得到的最小的表面积是多少。输入数据 N 为不超过 1000 的正整数。
设长宽高分别为 L,W,H ,题目要求就是 L*W*H=N 的情况下, 2*(L*W + L*H + W*H) 的最小值是多少。其中 L,W,H 都是自然数。不需要用数学来分析函数的极值。直接搜索即可。当 L 确定的情况下,问题变成了 W*H = N / L 为常数,求 W+H+W*H 的最小值的问题。枚举 W 即可确定 H 。代码如下:
#include <stdio.h>
int l, w, h;
void solve( int m) {
int t = 2147483647, tw;
for (w = 1; w <= m / 2 + 1; w++) {
if (m % w == 0) {
h = m / w;
if (w + h + w*h < t) {
t = w+h+w*h;
tw = w;
}
}
}
w = tw;
h = m / w;
}
int main() {
int c, n, ans, t;
scanf ( "%d" , &c);
while (c--) {
scanf ( "%d" , &n);
ans = 2147483647;
for (l = 1; l <= n/2 + 1; l++) {
if (n % l == 0) {
solve(n/l);
t = 2 * (l*w + l*h + w*h);
if (t < ans) ans = t;
}
}
printf ( "%d/n" , ans);
}
return 0;
}
JOJ 2237 Hero Ranklist: http://acm.jlu.edu.cn/joj/showproblem.php?pid=2237
Arthur 想要将手下的战士按照能力进行排名 , 选择出得力的助手进行冒险 . 他收集了很多人之间比武的胜负情况,如 A 战胜了 B , B 战胜了 C 等。他发现,这些胜负关系之间彼此互不矛盾,也就是不能出现 C 又战胜了 A 这种情况,但很多人之间并没有比武 , 也就无法衡量谁更强。 在这种情况下, Arthur 想把所有可能的排行榜都列举出来,供他进一步研究。
本题目包括多个 Case 。每个 Case 的第一行有 2 个数字 m,n. m<13 ,代表有多少个人物,每个人物使用一个大写字母表示。之后有 n 行 , n<100, 每一行有 2 个字母 X Y, 中间有一个减号 , 表示 X 战胜过 Y 。可以假定输入没有重复 .
这个题目就是拓扑排序,下面是用回溯写的拓扑排序代码:
void tsort( int step) {
int i, j;
if (step >= m) {
ans[step] = 0;
printf ( "%s/n" , ans);
return ;
}
for (i = 0; i < m; i++) {
if (in[i] == 0 && !visited[i]) {
visited[i] = 1;
for (j = 1; j <= Matrix[i][0]; j++) {
in[Matrix[i][j]]--;
}
ans[step] = 'A' + i;
tsort(step + 1);
for (j = 1; j <= Matrix[i][0]; j++) {
in[Matrix[i][j]]++;
}
visited[i] = 0;
}
}
}
给定一个正整数 N , N 不超过 40 ,问在 N 位由数字 0 、 1 组成的所有串中,没有连续 3 个 1 出现的串的个数是多少。比如 N 为 1 的时候,有 0 、 1 两个, N 为 2 的时候,有 00 、 01 、 10 、 11 四个, N 为 3 的时候, 111 就不合法了,此时这样串有 7 个。
解法:动态规划。所有的串都能按照最末两位分成四组。假设在 n 位合法的 0 、 1 串中,以 00 结尾的串的个数为 f0(n) ,以 01 结尾的串个数为 f1(n) ,以 10 结尾的串个数为 f2(n) ,以 11 结尾的串个数为 f3(n) ,则 n 位合法的 0 、 1 串的总数 f(n)=f0(n)+f1(n)+f2(n)+f3(n) 。当串的长度扩展到 n+1 位时,我们看这它们各自的个数如何动态变化。从 n 位到 n+1 位,只需要在 n 位串的末尾追加一位 0 或一位 1 即可。对四个函数分类讨论:
以 00 结尾:加 0 后变成以 00 结尾,加 1 后变成 01 结尾;
以 01 结尾:加 0 后变成以 10 结尾,加 1 后变成 11 结尾;
以 10 结尾:加 0 后变成以 00 结尾,加 1 后变成 01 结尾;
以 11 结尾:加 0 后变成以 10 结尾,加 1 后不合法。
所以,有: f0(n+1) = f0(n) + f2(n), f1(n+1) = f0(n) + f2(n), f2(n+1) = f1(n) + f3(n), f3(n+1) = f1(n) 。
当 n 为 2 时,有 f0(2) = f1(2) = f2(2) = f3(2) = 1 。这样,经过递推可以很快计算出结果。中间要考虑溢出的情况。当 n = 37 时,结果超过了 70 亿,必须用 double 类型。
JOJ 2488 Function Value: http://acm.jlu.edu.cn/joj/showproblem.php?pid=2488
函数 f(n) 如下定义:当 n=1 时, f(n) = 1 ;当 n 是一个素数时, f(n) = f(n – 1) ;否则, f(n) = f(k) + 1 ,其中 k 是 n 除自身以外最大的因子。输入数据 n 为不超过 2 的 31 次方的正整数。
这个题目直接按题意来做即可。因为 n 是素数时 n-1 ,这就变成了合数,然后再除以 k , k 至少是 2 ,所以 n 最起码被砍到了一半。所以这个题目的计算量就是 O(log(n)) 的。代码如下:
#include <stdio.h>
int main() {
unsigned int n, ans, k;
while ( scanf ( "%u" , &n) != EOF) {
ans = 0;
while (1) {
if (n == 1 || n == 2) { ans++; break ; }
for (k = 2; k * k <= n; k++)
if (n % k == 0) break ;
if (n % k) n--;
else { n /= k; ans++; }
}
printf ( "%u/n" , ans);
}
return 0;
}
JOJ 1868 Blocks: http://acm.jlu.edu.cn/joj/showproblem.php?pid=1868
有 N 个长宽高都为 1 的小立方体,把它们砌成一个长方体或正方体,能得到的最小的表面积是多少。输入数据 N 为不超过 1000 的正整数。
设长宽高分别为 L,W,H ,题目要求就是 L*W*H=N 的情况下, 2*(L*W + L*H + W*H) 的最小值是多少。其中 L,W,H 都是自然数。不需要用数学来分析函数的极值。直接搜索即可。当 L 确定的情况下,问题变成了 W*H = N / L 为常数,求 W+H+W*H 的最小值的问题。枚举 W 即可确定 H 。代码如下:
#include <stdio.h>
int l, w, h;
void solve( int m) {
int t = 2147483647, tw;
for (w = 1; w <= m / 2 + 1; w++) {
if (m % w == 0) {
h = m / w;
if (w + h + w*h < t) {
t = w+h+w*h;
tw = w;
}
}
}
w = tw;
h = m / w;
}
int main() {
int c, n, ans, t;
scanf ( "%d" , &c);
while (c--) {
scanf ( "%d" , &n);
ans = 2147483647;
for (l = 1; l <= n/2 + 1; l++) {
if (n % l == 0) {
solve(n/l);
t = 2 * (l*w + l*h + w*h);
if (t < ans) ans = t;
}
}
printf ( "%d/n" , ans);
}
return 0;
}
JOJ 2237 Hero Ranklist: http://acm.jlu.edu.cn/joj/showproblem.php?pid=2237
Arthur 想要将手下的战士按照能力进行排名 , 选择出得力的助手进行冒险 . 他收集了很多人之间比武的胜负情况,如 A 战胜了 B , B 战胜了 C 等。他发现,这些胜负关系之间彼此互不矛盾,也就是不能出现 C 又战胜了 A 这种情况,但很多人之间并没有比武 , 也就无法衡量谁更强。 在这种情况下, Arthur 想把所有可能的排行榜都列举出来,供他进一步研究。
本题目包括多个 Case 。每个 Case 的第一行有 2 个数字 m,n. m<13 ,代表有多少个人物,每个人物使用一个大写字母表示。之后有 n 行 , n<100, 每一行有 2 个字母 X Y, 中间有一个减号 , 表示 X 战胜过 Y 。可以假定输入没有重复 .
这个题目就是拓扑排序,下面是用回溯写的拓扑排序代码:
void tsort( int step) {
int i, j;
if (step >= m) {
ans[step] = 0;
printf ( "%s/n" , ans);
return ;
}
for (i = 0; i < m; i++) {
if (in[i] == 0 && !visited[i]) {
visited[i] = 1;
for (j = 1; j <= Matrix[i][0]; j++) {
in[Matrix[i][j]]--;
}
ans[step] = 'A' + i;
tsort(step + 1);
for (j = 1; j <= Matrix[i][0]; j++) {
in[Matrix[i][j]]++;
}
visited[i] = 0;
}
}
}