这几天专门挑些简单的算法题目做做,怕忘记,贴在这里。
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;
}
}
}