注册 登录
编程论坛 C++教室

[全民编程]76道高难度C++练习题.含NOI竞赛题.欢迎挑战

野比 发布于 2007-06-16 11:57, 48948 次点击

偶然从朋友那里搞到的, 名字是<<C++入门题>>
题目都相当的经典. 还有不少是NOI的考试题.
1 - 20题作为热身(见2楼), 越往后越难, 完整版(76道)请到1楼楼底下载.

请解决问题后, 不要吝惜你的才华, 把解题方法跟贴, 大家共同学习提高.

统计时间: 2007-10-10 19:29[/quote]

目前已完成题目(按发帖时间先后排序)

题号 完成人 楼层
---------------------------------------------
2 百年不亮 3
5 HCL 4
20 aipb2007 5,6
17 游乐园/aipb2007 18/20
6 HCL 26,27
16 jiaju111 33 讨论完成, 未编程
11 aipb2007 46
75 HJin 48 不符题干, 参考
49 HJin 49
48 HJin 61
10 HJin 62 * 218楼(blueboy82006),236楼(远去的列车)修改
13 HJin 63
17 HJin 65
1 HJin 67
3 tianma8778 69 有缺陷, 供参考
24 HJin 71
1 kai 77 另一种解法
2 kai 79 数字电路分析法
4 HJin 80
3 kai 81 # With analysis
19 HJin 83
3 laigaoat2005 69
37 HJin 103 162楼补充说明(smartwind)
67 HJin 104
74 HJin 105
4 wfpb 107 * + (kai指出, 见117楼)
3 smartwind 108 *
9 smartwind 109 *
8 wfpb 110 *
3 zkkpkk 111 *
12 wfpb 112 *
75 smartwind 113 *
37 weishj 114 *
41 zkkpkk 115 * 166楼改进(smartwind)
6 zkkpkk 118 * 130楼补充蛇形填数
65 weishj 124 *
14 huozoo 126 @ 正确
7 zkkpkk 137 *
76 HJin 138 *
4 天空の城 140 *
58 HJin 141 *
9 zkkpkk 142 *
13 zkkpkk 144 *
57 HJin 147 *
53 HJin 148 *
22 HJin 149 *
43 zkkpkk 150 *
21 HJin 152 *
31 smartwind 160 * 164楼补充(HJin) 165楼补充(smartwind)
34 smartwind 161 # 分析
45 smartwind 172 *
2 zhchchqihu 194,193 * 2种方法
3 freshman42 196 *
? blueboy82006 208 *
2 卧龙孔明 209,210 * 2种方法
1 hkice 211 *
5 hkice 212 *
3 miaomiao0403 214 *
10 ml232528 215 * 指出62楼10题错误
55 远去的列车 220 *
48 远去的列车 222 *
14 huozoo 223 * 解答者本人不确定
2,3 xjlsgcjdtc 228,229 *
3 qq598369 230 *
3 远去的列车 232 *
3 曦木 233 *
6 远去的列车 234 *
7 海子星竹 239 *
3 且行且珍惜 240 *


注:
* 未测试 (Not tested)
@ 已测试 (Tested)
+ Bug
# 无源代码 (No code)

完整版(76道)下载[attach]22592[/attach]


[此贴子已经被作者于2007-10-10 19:29:50编辑过]

288 回复
#52
野比2007-06-18 20:14
回复:(aipb2007) 11. 巧排数字。将1、2、......
aipb2007兄,.. 你的int main忘返回值了... 提醒一下
#53
野比2007-06-18 20:26
大家验证一下HJin关于第75题的解答吧.. 我持保留意见.. (没有完全按照题干作答)..
#54
aipb20072007-06-18 20:27
以下是引用野比在2007-6-18 20:14:36的发言:
aipb2007兄,.. 你的int main忘返回值了... 提醒一下

Can I write "void main()"?
The definition
void main() { /* ... */ }

is not and never has been C++, nor has it even been C. See the ISO C++ standard 3.6.1[2] or the ISO C standard 5.1.2.2.1. A conforming implementation accepts
int main() { /* ... */ }

and
int main(int argc, char* argv[]) { /* ... */ }

A conforming implementation may provide more versions of main(), but they must all have return type int. The int returned by main() is a way for a program to return a value to "the system" that invokes it. On systems that doesn't provide such a facility the return value is ignored, but that doesn't make "void main()" legal C++ or legal C. Even if your compiler accepts "void main()" avoid it, or risk being considered ignorant by C and C++ programmers.
In C++, main() need not contain an explicit return statement. In that case, the value returned is 0, meaning successful execution. For example:

#include<iostream>

int main()
{
std::cout << "This program returns the integer value 0\n";
}

Note also that neither ISO C++ nor C99 allows you to leave the type out of a declaration. That is, in contrast to C89 and ARM C++ ,"int" is not assumed where a type is missing in a declaration. Consequently:
#include<iostream>

main() { /* ... */ }

is an error because the return type of main() is missing.

摘自c++之父BJ大叔的FAQ。
请看红色部分
#55
野比2007-06-18 20:40
NEED not 不是 SHOULD not.. 也不是 BETTER not...
ISO/IEC 14882上面是这么说的:
If control reaches the end of main without encountering a return statement, the effect is that of executing
return 0;

但是谁又能保证看到这段代码的人都知道这个呢?
#56
aipb20072007-06-18 21:08


写习惯了,管他的,既然允许能少写点就少写点吧!
有人要编译那代码,也不会报错的。
#57
野比2007-06-18 21:18
当然, 这是个习惯问题..
就像我常写 void main 这种剑走偏锋的路子...

ps. VC编译会有warning的...对于任何没有返回的非void函数...
#58
aipb20072007-06-18 21:39
vc++6.0有太多bug和非标准行为了,我不喜欢它.
偶用vs2005!

[此贴子已经被作者于2007-6-18 21:45:35编辑过]

#59
野比2007-06-18 21:52
我的VC2005无法include! 为啥? 我就太阳了...
#60
bluebell2007-06-18 23:01

希望我自己完成的那天早些到来
^_^
#61
HJin2007-06-19 04:45

/*---------------------------------------------------------------------------
File name: C76-48.cpp
Author: HJin
Created on: 6/18/2007 15:15:52


C76-48.cpp --- the cpp source file for solving the 48th of the 76
classical C/C++ problems.

经典 76 道编程题 之 48:

48. 将4个红球,3个白球与3个黄球排成一排,共有多少种排法?

Answer: binomial(10, 4) * binomial(6, 3) * binomial(3, 3) = 4200.

Analysis:

Allocate r+w+y (=10) slots. The job can be done in three steps:
(1) we put all r red balls into the r+w+y slots --- total is binomial(r+w+y, r)
(2) we put all w white balls into the remaining w+y slots --- total is binomial(w+y, y)
(3) we put all y yellow balls into the remaining y slots --- total is binomial(y, y) = 1

All in all, total is binomial(r+w+y, r) * binomial(w+y, y) * 1.

Programming thoughts:

Generate all 3^(r+w+y) possible choices (since each slot can have one of the
three colors ) and check if a choice is valid. A choice is valid if and only
if there are r red balls, w white balls, and y yellow balls.

We used a set in the implementation, since set does not store duplicates.

Sample output:

1 rrwy
2 rryw
3 rwry
4 rwyr
5 ryrw
6 rywr
7 wrry
8 wryr
9 wyrr
10 yrrw
11 yrwr
12 ywrr
total number of different combinations are 12
Press any key to continue . . .


Reference:

1. 野比, 相当经典的76道编程自虐题分享.无答案 at
https://bbs.bc-cn.net/viewthread.php?tid=147853
*/

#include <iostream>
#include <set>
#include <string>
using namespace std;

string buffer;

/**
Time complexity is O(3^(r+w+y)) --- very inefficient.

build the set of all combinations.
*/
void buildSet(int r, int w, int y, int n, set<string>& s);

/**
wrap the function buildSet() and outputs results.
*/
int combination(int r, int w, int y, bool print = true);
/**
check if the string is valid.

A choice is valid if and only if there are r red balls, w white balls, and
y yellow balls.
*/
bool isValid(int r, int w, int y, string& str);


int main(int argc, char** argv)
{
// test #3
//int r=4;
//int w=3;
//int y=3;

// test #2
//int r=3;
//int w=2;
//int y=2;

// test #1
int r=2;
int w=1;
int y=1;

cout<<"total number of different combinations are "<<combination(r, w, y, true)<<endl;

return 0;
}

void buildSet(int r, int w, int y, int n, set<string>& s)
{
if(n==0)
{
if(isValid(r, w, y, buffer))
{
s.insert(buffer);
}

return;
}

string temp = buffer; // keep previous state

buffer+="r";
buildSet(r, w, y, n-1, s);

buffer = temp; // recall previous state
buffer+="w";
buildSet(r, w, y, n-1, s);

buffer = temp; // recall previous state
buffer+="y";
buildSet(r, w, y, n-1, s);
}

int combination(int r, int w, int y, bool print)
{
set<string> s;
int n = r+w+y;
int counter = 0;

buildSet(r, w, y, n, s);

if(print) // print combinations
{
for(set<string>::const_iterator iter = s.begin(); iter!=s.end(); ++iter)
{
++counter;
cout<<counter<<"\t"<<*iter<<endl;
}
}

return s.size();
}

bool isValid(int r, int w, int y, string& str)
{
int r_=0; // store # of red balls
int w_=0;
int y_=0;
bool res = false;

for(size_t i =0; i<str.size(); ++i)
{
switch(str[i])
{
case 'r':
++r_;
break;
case 'w':
++w_;
break;
case 'y':
++y_;
break;
}
}

res = (r_==r) && (w_==w) && (y_==y);

return res;
}

[此贴子已经被作者于2007-6-19 6:17:01编辑过]

#62
HJin2007-06-19 06:55

/*---------------------------------------------------------------------------
File name: C76-10.cpp
Author: HJin
Created on: 6/18/2007 15:15:52


C76-10.cpp --- the cpp source file for solving the 10th of the 76
classical C/C++ problems.

经典 76 道编程题 之 10:

10. 如图1所示,编写程序计算 ┎┰┰┰┰┰┰┰┰┰┒
大大小小正方形共有多少?当最小 ┠╂╂╂╂╂╂╂╂╂┨
正方行边长为1时,它们的总面积 ┠╂╂╂╂╂╂╂╂╂┨
共为多少? ┠╂╂╂╂╂╂╂╂╂┨
┠╂╂╂╂╂╂╂╂╂┨
┠╂╂╂╂╂╂╂╂╂┨
┠╂╂╂╂╂╂╂╂╂┨
┠╂╂╂╂╂╂╂╂╂┨
┠╂╂╂╂╂╂╂╂╂┨
┠╂╂╂╂╂╂╂╂╂┨
┖┸┸┸┸┸┸┸┸┸┚

Analysis:

i | # of squares with side length i | total area
----+------------------------------------------------
n | 1^2 | n*1^2
n-1 | 2^2 | (n-1)*2^2
n-2 | 3^2 | (n-2)*3^2
.....................................................
2 | (n-2)^2 | 2*(n-2)^2
1 | n^2 | 1*n^2

total number of squares is 1^2 + 2^2 +... +n^2 = n*(n+1)*(2*n+1)/6
total area is 1*n^2 + 2*(n-1)^2 + ... + n*1^2 = n*(n+1)^2*(n+2)/12


Sample output:

n | # of squares | total area | # of squares 2 | total area 2
-----+--------------+--------------+----------------+---------------
1 | 1 | 1 | 1 | 1
2 | 5 | 6 | 5 | 6
3 | 14 | 20 | 14 | 20
4 | 30 | 50 | 30 | 50
5 | 55 | 105 | 55 | 105
6 | 91 | 196 | 91 | 196
7 | 140 | 336 | 140 | 336
8 | 204 | 540 | 204 | 540
9 | 285 | 825 | 285 | 825
10 | 385 | 1210 | 385 | 1210
11 | 506 | 1716 | 506 | 1716
12 | 650 | 2366 | 650 | 2366
13 | 819 | 3185 | 819 | 3185
14 | 1015 | 4200 | 1015 | 4200
15 | 1240 | 5440 | 1240 | 5440
16 | 1496 | 6936 | 1496 | 6936
17 | 1785 | 8721 | 1785 | 8721
18 | 2109 | 10830 | 2109 | 10830
19 | 2470 | 13300 | 2470 | 13300
-----+--------------+--------------+----------------+---------------
Press any key to continue . . .

Reference:

1. 野比, 相当经典的76道编程自虐题分享.无答案 at
https://bbs.bc-cn.net/viewthread.php?tid=147853
*/

#include <iostream>
#include <iomanip>
using namespace std;

int numOfSquares(int n);
int totalArea(int n);

int numOfSquares2(int n);
int totalArea2(int n);

int main(int argc, char** argv)
{
cout<<"n | # of squares | total area | # of squares 2 | total area 2\n";
cout<<"-----+--------------+--------------+----------------+---------------\n";

for(int i=1; i<20; ++i)
cout<<std::left<<setw(4)<<i<<" | "
<<setw(12)<<numOfSquares(i)<<" | "
<<setw(12)<<totalArea(i)<<" | "
<<setw(14)<<numOfSquares2(i)<<" | "
<<setw(12)<<totalArea2(i)
<<endl;
cout<<"-----+--------------+--------------+----------------+---------------\n";

return 0;
}

int numOfSquares(int n)
{
int sum =0;
for(int i=1; i<=n; ++i)
sum += i*i;

return sum;
return n*(n+1)*(2*n+1)/6;
}

int numOfSquares2(int n)
{
// don't write n/6*(n+1)*(2*n+1)
// this could give you 0, since 1/6 = 0
return n*(n+1)*(2*n+1)/6;
}

int totalArea(int n)
{
int area = 0;

for(int i=1; i<=n; ++i)
{
area += i*(n+1-i)*(n+1-i);
}

return area;
}

int totalArea2(int n)
{
return n*(n+1)*(n+1)*(n+2)/12;
}

#63
HJin2007-06-19 07:31

/*---------------------------------------------------------------------------
File name: C76-13.cpp
Author: HJin
Created on: 6/18/2007 15:15:52


C76-13.cpp --- the cpp source file for solving the 13th of the 76
classical C/C++ problems.

经典 76 道编程题 之 13:

13. 有N个硬币(N为偶数)正面朝上排成一排,每次将 N-1 个硬币翻过来放在原位
置, 不断地重复上述过程,直到最后全部硬币翻成反面朝上为止。编程让计算机把
翻币的最简过程及翻币次数打印出来(用*代表正面,O 代表反面)。


Analysis:

Since we are changing the states of N-1 items each time, there is one coin which
keeps its previous state.

1 --- 正面 (or *)
0 --- 反面

1 1 1 1 (original state: #0)
0 0 0 1 (#1 --- keeps N = 4)
1 1 0 0 (#2 --- keeps N-1 = 3)
0 1 1 1 (#3 --- keeps N-2 = 2)
0 0 0 0 (#4 --- keeps N-3 = 1)

This is true for the general case N.


Sample output:

* * * *
0 0 0 *
* * 0 0
0 * * *
0 0 0 0
Press any key to continue . . .

Reference:

1. 野比, 相当经典的76道编程自虐题分享.无答案 at
https://bbs.bc-cn.net/viewthread.php?tid=147853
*/


#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;

void print(const vector<bool>& v);
int tossCoins(int n);


int main(int argc, char** argv)
{
tossCoins(4);

return 0;
}

int tossCoins(int n)
{
vector<bool> v;
int i;
int j;

// orginal state
v.resize(n);
for(i=0; i<n; ++i)
v[i] = true;
print(v);

for(i=n-1; i>=0; --i)
{
for(j=0; j<n; ++j)
v[j] = !v[j];
v[i] = !v[i]; // keeps v[i]

print(v);
}

return n;
}

void print(const vector<bool>& v)
{
for(vector<bool>::const_iterator iter = v.begin(); iter != v.end(); ++iter)
{
if(*iter)
cout<<setw(4)<<"*";
else
cout<<setw(4)<<"0";
cout<<" ";
}
cout<<endl;
}

#64
aipb20072007-06-19 08:24
#65
HJin2007-06-19 09:21

/*---------------------------------------------------------------------------
File name: C76-17.cpp
Author: HJin
Created on: 6/18/2007 18:21:10


C76-17.cpp --- the cpp source file for solving the 17th of the 76
classical C/C++ problems.

经典 76 道编程题 之 17:

17. 编写一个程序,当输入不超过60个字符组成的英文文字时,计算机将这个句子
中的字母按英文字典字母顺序重新排列,排列后的单词的长度要与原始句子中的长度
相同。例如:

输入:

THE PRICE OFBREAD IS ¥1 25 PER POUND

输出:

ABC DDEEE EFHIINO OP ¥1 25 PPR RRSTU

并且要求只对A到Z的字母重新排列,其它字符保持原来的状态。


Analysis:

This is really a counting sort problem --- count how many letters A to Z.


Sample output:

THE PRICe OFBREAD IS $1.25 PER POUND
ABC DDEEE EFHIINO OP $1.25 PPR RRSTU
Press any key to continue . . .


Reference:

1. 野比, 相当经典的76道编程自虐题分享.无答案 at
https://bbs.bc-cn.net/viewthread.php?tid=147853
*/

#include <iostream>
#include <iomanip>
using namespace std;

/**
counting sort to sort the letters 经典 76 道编程题 之 17.

Space complexity: O(1).
Time complexity: O(strlen(s)).
*/
void counting_sort(char* s);

int main(int argc, char** argv)
{
char s[] = "THE PRICe OFBREAD IS $1.25 PER POUND";

cout<<s<<endl;

counting_sort(s);
cout<<s<<endl;

return 0;
}

void counting_sort(char* s)
{
/**
c[0] --- stores how many letter A in s
c[1] --- stores how many letter B in s
...
c[25] --- stores how many letter Z in s
*/
int c[26];
int n =strlen(s);
int i;
int j;

// initiate counters to 0
for(j=0; j<26; ++j)
{
c[j] = 0;
}

for(i=0; i<n; ++i)
{
// toupper() does not affect non-alpha letters.
s[i] = toupper(s[i]);
}

// count number of different capital letters
for(i=0; i<n; ++i)
{
if(s[i] >='A' && s[i] <='Z')
{
j = 0+ (s[i] - 'A') ;
++c[ j ];
}
}

i=0;

/**
Although there are two loops here, it is NOT O(25* n).
It is O(n), since i is incremented c[j] times for each j,
and c[0] + c[1] + ... +c[25] <= n.
*/
for(j=0; j<25; ++j)
{
while (c[j] > 0)
{
if(s[i]>='A' && s[i]<='Z')
{
s[i] = 'A'+j;
--c[j]; // used 1 letter, so counter goes down by 1
}
++i;
}
}
}

#66
HCL2007-06-19 12:50

哇~这么强啊!!!厉害

#67
HJin2007-06-19 14:02

/*---------------------------------------------------------------------------
File name: C76-1.cpp
Author: HJin
Created on: 6/18/2007 22:53:55


C76-1.cpp --- the cpp source file for solving the 1st of the 76
classical C/C++ problems.

经典 76 道编程题 之 1:

1. 给定等式 A B C D E 其中每个字母代表一个数字,且不同数字对应不
D F G 同字母。编程求出这些数字并且打出这个数字的
+ D F G 算术计算竖式。

───────

X Y Z D E


Analysis:

Let A = a[0], B = a[1], ..., Z = a[9], where the array a
is a permutation of the ten digits. The easist way is to
loop over all 10! = 3,628,800 possible permutations for the array a,
and check if the result holds.

C++ has a next_permutation (or prev_permutation) function template
for permutations.


A B C D E F G X Y Z
==============================
a[ 0 1 2 3 4 5 6 7 8 9 ]

Sample output:

A = 2, B = 9, C = 7, D = 8, E = 6,
F = 5, G = 0, X = 3, Y = 1, Z = 4.

2 9 7 8 6
8 5 0
+ 8 5 0
--------------------------------------
= 3 1 4 8 6
Press any key to continue . . .

Reference:

1. 野比, 相当经典的76道编程自虐题分享.无答案 at
https://bbs.bc-cn.net/viewthread.php?tid=147853
*/

#include <iostream>
#define DIM(x) sizeof((x)) / sizeof ( (x)[0] )
#include <algorithm>

using namespace std;

/**
check the result for a permutation.
*/
bool check(int *a);

/**
print result.
*/
void print(int *a);

int main(int argc, char** argv)
{
int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
bool res = check(a);
if(res)
print(a);

// when next_permutation returns false, we permute
// all 10! choices. Time complexity is O(10!)
while( std::next_permutation(a, a+DIM(a)) )
{
res = check(a);
if(res)
print(a);
}

return 0;
}

bool check(int *a)
{
int carry; // carry
bool res;
int temp;

// 1st vertical equation
temp = a[4] + 2*a[6];
res = ( ( temp % 10) == a[4] );
if(!res)
return false;
carry = temp / 10;

// 2nd vertical equation
temp = a[3] + 2*a[5] + carry;
res = res && ( (temp % 10) == a[3] );
if(!res)
return false;
carry = temp / 10;

// 3rd vertical equation
temp = a[2] + 2*a[3] + carry;
res = res && ( ( temp % 10) == a[9] );
if(!res)
return false;
carry = temp / 10;

// 4th vertical equation
temp = a[1] + carry;
res = res && ( ( temp % 10) == a[8] );
if(!res)
return false;
carry = temp / 10;

// 5th vertical equation
temp = a[0] + carry;
res = res && ( ( temp % 10) == a[7] );
if(!res)
return false;

return true;
}

void print(int *a)
{
cout<<"A = "<<a[0]<<", ";
cout<<"B = "<<a[1]<<", ";
cout<<"C = "<<a[2]<<", ";
cout<<"D = "<<a[3]<<", ";
cout<<"E = "<<a[4]<<",\n";
cout<<"F = "<<a[5]<<", ";
cout<<"G = "<<a[6]<<", ";
cout<<"X = "<<a[7]<<", ";
cout<<"Y = "<<a[8]<<", ";
cout<<"Z = "<<a[9]<<".\n\n";

cout<<" "<<a[0]<<" "<<a[1]<<" "<<a[2]<<" "<<a[3]<<" "<<a[4]<<"\n";
cout<<" "<<a[3]<<" "<<a[5]<<" "<<a[6]<<"\n";
cout<<"+ "<<a[3]<<" "<<a[5]<<" "<<a[6]<<"\n";
cout<<"--------------------------------------\n";
cout<<"= "<<a[7]<<" "<<a[8]<<" "<<a[9]<<" "<<a[3]<<" "<<a[4]<<"\n";
}

#68
HJin2007-06-19 14:14
I just saw aipb2007's solution to #17 --- very clever. Although your space complexity is 2n, the algorithm is easy to follow. Time complexity is O(n lg n ), n = strlen(s) --- since you used a sort (the standard algorithm) which is a comparison sorting, which is necessarily eat least O(n lg n).

My soln is a little hard to understand for a beginner, but it uses only strlen(s) + const space and most importantly, time compelexity is only O(n).

[此贴子已经被作者于2007-6-21 5:56:20编辑过]

#69
tianma87782007-06-19 16:26

第三题挑个简单的

#include "stdio.h"

main()
{
char s[21][21];
char c[]={'T','J','1','2','3','4','5','6','7','8','9'};
int input,i,j;
printf("enter the number betweent 3 to 20");
scanf("%d",&input);
if(input<3||input>20)
{
printf("what you want ");
getch();
return;
}
for(i=0;i<=input;i++)
{
for(j=0;j<=input;j++)
s[i][j]='0';

}
for(i=0;i<=input;i++)
{
for(j=0;j<=input;j++)
printf("%c",s[i][j]);
printf("\n");
}

for(i=input;i>=0;i--)
{
for(j=i;j<=input-i;j++)
{
s[j][i]=c[i];
s[i][j]=c[i];
s[input-i][input-j]=c[i];
s[input-j][input-i]=c[i];
}

}
for(i=0;i<=input;i++)
{
for(j=0;j<=input;j++)
printf("%c",s[i][j]);
printf("\n");
}
getch();
}

#70
aipb20072007-06-19 16:28
To HJin :
Actually I know a little abt algorithms, but I'm really interesting in it. Most of these questions are base on an algorithm, I just found the way to get the answer, abt the efficiency, I didn't consider it.

Recently I have to prepare for the examination.So I have no time to do the subjects.I'll study algorithms during summer holiday. After that I think I can catch your step.

I will see all your solutions above when I come to the same subject.
#71
HJin2007-06-19 17:06

/*---------------------------------------------------------------------------
File name: C76-24.cpp
Author: HJin
Created on: 6/18/2007 22:53:55


C76-24.cpp --- the cpp source file for solving the 24th of the 76
classical C/C++ problems.

经典 76 道编程题 之 24:

24. 某地街道把城市分割成矩形方格,每一方格叫作块,某人从家中出发上班,
向东要走M块,向北要走N块,(见图)。请设计一个程序,由计算机寻找并
打印出所有的上班的路径。

单位

┬ ┌─┬─┬─┬─┬─┬─┬─┐
│ │ │ │ │ │ │ │ │
│ ├─┼─┼─┼─┼─┼─┼─┤
↓ │ │ │ │ │ │ │ │
N ├─┼─┼─┼─┼─┼─┼─┤
↑ │ │ │ │ │ │ │ │
│ ├─┼─┼─┼─┼─┼─┼─┤
│ │ │ │ │ │ │ │ │
┴ └─┴─┴─┴─┴─┴─┴─┘
家 ├─────→M←─────┤


Analysis:

Let us

E --- the person move 1km to the east;
N --- to stand for the person move 1km to the east;

If M= 7, N =4 (as in the figure), then

E E E E E E E N N N N

means that the person travels east 7km first, then he/she travels
north 4km.

A little bit math shows that we have

binomial(M+N, M)

different ways to travel from home to work, since we have to travel
M kms to the east. A way is a choice that how we put the M E's into
M+N slots.

Sample output (for M=5, N=2):

1 EEEEENN
2 EEEENEN
3 EEEENNE
4 EEENEEN
5 EEENENE
6 EEENNEE
7 EENEEEN
8 EENEENE
9 EENENEE
10 EENNEEE
11 ENEEEEN
12 ENEEENE
13 ENEENEE
14 ENENEEE
15 ENNEEEE
16 NEEEEEN
17 NEEEENE
18 NEEENEE
19 NEENEEE
20 NENEEEE
21 NNEEEEE
correct.
Press any key to continue . . .


Reference:

1. 野比, 相当经典的76道编程自虐题分享.无答案 at
https://bbs.bc-cn.net/viewthread.php?tid=147853
*/

#include <iostream>
#include <string>
#include <set>
using namespace std;

/**
serious improvment needed: this recursive version is very inefficient.
For M=7, N=4, it takes 3 mintues to compute.

The classical algorithm to permute a string. We use a set to keep
different strings --- some permutations may repeat.

*/
void way_recursive(string& in, string& out, set<string>& ss);

/**
a wrapper of way_recursive.
*/
int way(int M, int N, bool print=true);

/** compute binomial(n, k).
* n >=k
*
*
*/
int binomial(int n, int k); // n choose k


int main(int argc, char** argv)
{
// test #1: --- the recursive way takes long time (3 mintues)
//int M=7;
//int N=4;


// test #2:
int M=5;
int N=2;

int num;

num = way(M, N);


/**
A little bit math shows that the number is binomial(M+N, M).
This is just a check to give you more confidence.
*/
if(num != binomial(M+N, M))
cout<<"wrong number.\n";
else
cout<<"correct.\n";

return 0;
}

void way_recursive(string& in, string& out, set<string>& ss)
{
// in is empty so that all chars are transferred to out buffer
// and out is a permutation of in
if( in.empty() )
{
ss.insert(out); // insert only one copy --- no duplicates
return;
}

// the way to permute a string in one statement
for(size_t i=0; i<in.size(); ++i)
way_recursive(in.substr(0, i) + in.substr(i+1), out+in[i], ss);
}

int way(int M, int N, bool print)
{
set<string> ss;
string in;
string out;
int i;
int counter = 0;

in.resize(M+N);

// the very first way
for(i=0; i<M; ++i)
in[i] = 'E';
for(i=M; i<M+N; ++i)
in[i] = 'N';

// build the set
way_recursive(in, out, ss);

if(print)
{
for(set<string>::const_iterator iter = ss.begin(); iter != ss.end(); ++iter)
{
++counter;
cout<<counter<<"\t"<<*iter<<endl;
}
}

return counter;
}

int binomial(int n, int k)
{
int res = 1;
int i;

if(k>n/2)
k = n-k;

++n;
for(i=1; i<=k; ++i)
{
res *= (n-i);
res /= i;
}

return res;
}

#72
yuyunliuhen2007-06-19 17:17
To everybody:
You are good example,I have learned so much from you . I am not good at arithmetic,so I have many things to learn.As aipb2007,I am plan to study arithmetic in the hoilday.Arithmetic is interesting,and I like it.
Exam is coming,I think you are prepareing for exam.so do I.Good luck!
At last,Happy Dragon Boat Festival!

[此贴子已经被作者于2007-6-19 17:20:02编辑过]

#73
HJin2007-06-19 17:26

Happy holiday to you! Admire you have "zhongzi"..... no Chinese food in this small town for me.

#74
野比2007-06-19 19:46
HJin...I 服了 You... 受我一拜.. orz

继续消化...
#75
aipb20072007-06-19 20:39
To HJin :
Where are you?
#76
love1541392007-06-20 13:14

都是高手啊。...参考参考

[此贴子已经被作者于2007-6-20 13:18:05编辑过]

#77
kai2007-06-20 21:28
关于第一题给出另一种解法:

ABCDE + (DFG)*2 = XYZDE <=> (DFG)*2 = XYZDE - ABCDE <=> (XYZ - ABC)*100
<=> DFG = (XYZ - ABC)*50
由此可以看出,E可以取任何数字,只要其他的字母的组合有解。

下面是代码:

[CODE]
#include <iostream>
using namespace std;

int main()
{
int A, B, C, D, E, F, G, X, Y, Z;
int value1, value2, value3;

cout<<"A B C D E F G X Y Z\n";
for(A = 0; A<=8; A++)
{
for(X = A+1; X<=9; X++)
{
for(B = 0; B<=9; B++)
{
if(B==A || B==X)
continue;
for(C = 0; C<=9; C++)
{
if(C==A || C==X || C==B)
continue;
for(Y = 0; Y<=9; Y++)
{
if(Y==A || Y==X || Y==B || Y==C)
continue;
for(Z = 0; Z<=9; Z++)
{
if(Z==A || Z==X || Z==B || Z==C || Z==Y)
continue;
for(D = 0; D<=9; D++)
{
if(D==A || D==X || D==B || D==C || D==Y || D==Z)
continue;
for(F = 0; F<=9; F++)
{
if(F==A || F==X || F==B || F==C || F==Y || F==Z || F==D)
continue;
for(G = 0; G<=9; G++)
{
if(G==A || G==X || G==B || G==C || G==Y || G==Z || G==D || G==F)
continue;
value1 = X*100 + Y*10 + Z;
value2 = A*100 + B*10 + C;
value3 = D*100 + F*10 + G;
if(value3 == ((value1-value2)*50))
{
for(E = 0; E<=9; E++)
{
cout<<A<<" "<<B<<" "<<C<<" "<<D<<" "<<E<<" "<<F<<" "<<G<<" "<<X<<" "<<Y<<" "<<Z<<endl;
}
}
}
}
}
}
}
}
}
}
}

system("pause");
return 0;
}

[/CODE]
这样的代码看上去笨了些,for loop 一个套一个,但是免去了很多的测试,效率应该高一些。
#78
野比2007-06-20 22:05
有点意思....
#79
kai2007-06-24 05:23
关于第二题,其实是数字电路的问题。5个条件就是第一层的5个门电路,将5个门电路的输出结果在第二层通过一个与门就可以得到最终的结果了。所以代码是很简单的,其中0表示不参加,1表示参加.

listing2.cpp
[CODE]
#include <iostream>
using namespace std;

int main()
{
int A,B,C,D,E;
int out1, out2, out3, out4, out5;
int out;
cout<<"A B C D E\n";
for(A = 0; A<=1; A++)
{
for(B = 0; B<=1; B++)
{
for(C = 0; C<=1; C++)
{
for(D = 0; D<=1; D++)
{
for(E = 0; E<=1; E++)
{
out1 = (A&B) | (!A);
out2 = (!B) | (!C);
out3 = (C&D) | ((!C)&(!D));
out4 = D | E;
out5 = (E & A & D) | (!E);
out = out1 & out2 & out3 & out4 & out5;
if(out == 1)
cout<<A<<" "<<B<<" "<<C<<" "<<D<<" "<<E<<endl;
}
}
}
}
}
return 0;
}

[/CODE]
#80
HJin2007-06-24 06:51

/*---------------------------------------------------------------------------
File name: C76-4.cpp
Author: HJin
Created on: 6/22/2007 19:33:55

Modification history:

6/23/2007 14:51:02
-----------------------------------------------------------
Apply a recursive technique to construct all Latin Squares of
size n<=6.

This recursive approach gives all the solutions, although it is
fairly slow due to the large number of solutions. For n=7, there
are 61,479,419,904,000 Latin squares. The number 61,479,419,904,000
overflows the integers for 32-bit system.

I consider this problem solved.


6/22/2007 22:34:40
-----------------------------------------------------------
First attack: generate n! Latin squares.

This iterative approach only gives a partial soln, but it is
fast.


C76-4.cpp --- the cpp source file for solving the 4th of the 76
classical C/C++ problems.


经典 76 道编程题 之 4:

4. 在N行N列的数阵中, 数K(1〈=K〈=N)在每行和每列中出现且仅
出现一次,这样的数阵叫N阶拉丁方阵。例如下图就是一个五阶拉丁方阵。
编一程序,从键盘输入N值后,打印出所有不同的N阶拉丁方阵,并统计个数。

1 2 3 4 5
2 3 4 5 1
3 4 5 1 2
4 5 1 2 3
5 1 2 3 4

Analysis:

This problem is fairly complex, as it is unsolved for the general case n.
For n<=10, I give the known number of different Latin squares in the
following table


n | # of Latin squares
----+------------------------------------------------------
1 | 1
2 | 2
3 | 12
4 | 576
5 | 161280
6 | 812851200
7 | 61479419904000
8 | 108776032459082956800
9 | 5524751496156892842531225600
10 | 9982437658213039871725064756920320000
11 | 776966836171770144107444346734230682311065600000
>11 | (no one knows the exact number)
----+------------------------------------------------------

If you want only partial solutions for a fairly large n, please check my

generate_iterative()

procedure, which is based on rotating a permuation of 1 2 ... n.

If you want all solutions for n<=6, please check my

generate_recursive()

procedure.


Sample output

(For test #1, n = 5, only 3 Latin squares are shown.)
Latin squares #161278
5 4 3 2 1
4 5 2 1 3
3 2 1 4 5
2 1 5 3 4
1 3 4 5 2
Latin squares #161279
5 4 3 2 1
4 5 2 1 3
3 2 1 5 4
1 3 5 4 2
2 1 4 3 5
Latin squares #161280
5 4 3 2 1
4 5 2 1 3
3 2 1 5 4
2 1 4 3 5
1 3 5 4 2
There are 161280 different Latin Squares for n = 5
Press any key to continue . . .


(For test #2, n = 7, only 3 Latin squares are shown.)
Latin Square #5038
7 6 5 4 2 3 1
5 4 6 2 3 1 7
6 2 4 3 1 7 5
4 3 2 1 7 5 6
2 1 3 7 5 6 4
3 7 1 5 6 4 2
1 5 7 6 4 2 3
Latin Square #5039
7 6 5 4 3 1 2
5 7 4 3 1 2 6
4 5 3 1 2 6 7
3 4 1 2 6 7 5
1 3 2 6 7 5 4
2 1 6 7 5 4 3
6 2 7 5 4 3 1
Latin Square #5040
7 6 5 4 3 2 1
6 5 4 3 2 1 7
5 4 3 2 1 7 6
4 3 2 1 7 6 5
3 2 1 7 6 5 4
2 1 7 6 5 4 3
1 7 6 5 4 3 2
Press any key to continue . . .

Reference:

1. 野比, 相当经典的76道编程自虐题分享.无答案 at
https://bbs.bc-cn.net/viewthread.php?tid=147853

2. http://en.wikipedia.org/wiki/Latin_square
*/

#include <iostream>
#include <iomanip>
#include <algorithm>

using namespace std;


/** buffer for 2d array (n! x n)
This buffer stores all the n! permuations for 1 2 ... n.
You can use a set.

If you want to generate all the permuatations along the way,
you spend more time. My technique is sacrifice space in
favor of speed.
*/
int** g_AllPerms;
int g_nFact; // n!

/**
A recursive algorithm to get all the Latin squares of size n.

The number of different Latin squares of size n is stored in counter.

Space complexity: O( (n+1)! ).
Time complexity: O( (n+1)! ).

*/
void generate_recursive(int** a, int n, int numRSF, int& counter, bool print = false);


/**
This generator can only generate n! different Latin squares. Thus,
it is a partial soln.

I cannot describe the idea in plain text format, you may referr to reference 2
and google for "Latin square".

Space complexity: O( (n+1)! ).
Time complexity: O( (n+1)! ).
*/
void generate_iterative(int** a, int* buffer, int n, bool print=true);

/**
Rotate a sequence one position to the left. For example,

1 2 3 4 --> 2 3 4 1.
*/
void rotate(int *aPerm, int n);


/**
Check if the first numRSF rows of a matrix is valid for a Latin
square.

numRSF --- number of Rows So Far
*/
inline bool isValid(int**a, int n, int numRSF);

/**
Copy the content of a buffer to another.
*/
inline void copy(int*dest, int* src, int n);

/**
Compute the facotrial of n.
*/
inline int factorial(int n);

/**
auxilliary functions for 2d array dynamic allocation and deallocation.
*/
template <typename T>
inline T** new2d(int row, int col);

template <typename T>
inline void delete2d(T** arr2d, int row);


int main()
{
/**
All cases for n > 6 cannot be solved by the recursive version, using
32-bit intergers.

If you use the iterative version, you can let n be fairly large.
*/
int n=4;
int i;
int* buf; // buffer for 1d array of size n
int** a;
int counter = 0;

g_nFact = factorial(n);

buf = new int[n];
for(i=0; i<n; ++i)
{
buf[i] = i+1;
}

a = new2d<int>(n, n);
g_AllPerms = new2d<int>(g_nFact, n);

i=0;
copy(g_AllPerms[i], buf, n);
while(next_permutation(buf, buf+n))
{
++i;
copy(g_AllPerms[i], buf, n);
}

// test #1 --- recursive (complete soln)
generate_recursive(a, n, 0, counter, true);
cout<<"There are "<<counter<<" different Latin Squares for n = "<<n<<endl;


// test #2 --- iterative (partial soln)
generate_iterative(a, buf, n);


delete [] buf;
delete2d<int>(a, n);
delete2d<int>(g_AllPerms, n);

return 0;
}

void generate_recursive(int** a, int n, int numRSF, int& counter, bool print)
{
// if all rows are filled
if(numRSF == n )
{
++counter; // update counter

if(print) // print this Latin square
{
cout<<"Latin squares #"<<counter<<endl;
for(int i=0; i<n; ++i)
{
for(int j=0; j<n; ++j)
{
cout<<setw(4)<<a[i][j];
}
cout<<endl;
}
}

return;
}

for(int k=0; k<g_nFact; ++k)
{
for(int i=numRSF; i<n; ++i)
{
// fill in the current row
copy(a[i], g_AllPerms[k], n);

// if the matrix is valid up to row i
// then we fill in the next row
if(isValid(a, n, i))
generate_recursive(a, n, i+1, counter, print);
}
}
}

void generate_iterative(int** a, int* buf, int n, bool print)
{
int i;
int j;
int k;

for(k=0; k<g_nFact; ++k)
{
copy(buf, g_AllPerms[k], n);
for(i=0; i<n; ++i)
{
int value = g_AllPerms[k][buf[0]-1];

for(int j=0; j<n; ++j)
{
a[j][ buf[j]-1 ] = value;
}

rotate(buf, n);
}

// print the matrix
cout<<"Latin Square #"<<k+1<<endl;
for(i=0; i<n; ++i)
{
for(j=0; j<n; ++j)
{
cout<<setw(4)<<a[i][j];
}
cout<<endl;
}
}
}

void rotate(int *aPerm, int n)
{
int i;
int temp = aPerm[0];

for(i=0; i<n-1; ++i)
aPerm[i] = aPerm[i+1];

aPerm[n-1] = temp;
}

bool isValid(int**a, int n, int numRSF)
{
int i;
int j;

for(i=0; i<numRSF; ++i)
{
for(j=0; j<n; ++j)
{
if(a[i][j] == a[ numRSF ] [j])
{
return false;
}
}
}

return true;
}

void copy(int*dest, int* src, int n)
{
int i;

for(i=0; i<n; ++i)
dest[i] = src[i];
}

int factorial(int n)
{
int res = 1;

for(int i=2; i<=n; ++i)
res *= i;

return res;
}


template <typename T>
T** new2d(int row, int col)
{
T** arr2d = new T*[row];
for(int i=0; i<row; ++i)
arr2d[i] = new T[col];

return arr2d;
}


template <typename T>
void delete2d(T** arr2d, int row)
{
for(int i=0; i<row; ++i)
delete [] arr2d[i];

delete [] arr2d;
}

[此贴子已经被作者于2007-6-24 7:23:26编辑过]

#81
kai2007-06-24 07:30
关于第三题, 如果仔细观察一下这个图,那么就是每一层的字符都是一样的,这样的话,我们只要实现兜圈子的画法,那么下一层只是一个起点和终点的数据变更. 我们知道了 N*N 的矩形, 那么一共要画几个圈子就知道了, 那就是 (N+1)/2;

listing3.cpp
[CODE]
#include <iostream>
using namespace std;

int main()
{
int N = 15; // to test the situation, you can change the N value;
int x, y;
int i = 0;
char c[N][N];
char b = 'T';

while(i<((N+1)>>1))
{
for(x = i, y = i; x<(N-1-i); x++)
{
if(i == 0)
b = 'T';
else if(i == 1)
b = 'J';
else
{
b = '1' + i - 2;
}
c[x][y] = b;
}
for(x = N - 1 - i, y = i; y<(N-1-i); y++)
{
if(i == 0)
b = 'T';
else if(i == 1)
b = 'J';
else
{
b = '1' + i - 2;
}
c[x][y] = b;
}
for(x = N - 1 - i, y = N - 1 - i; x>=i; x--)
{
if(i == 0)
b = 'T';
else if(i == 1)
b = 'J';
else
{
b = '1' + i - 2;
}
c[x][y] = b;
}
for(x = i, y = N - 1 - i; y>=i; y--)
{
if(i == 0)
b = 'T';
else if(i == 1)
b = 'J';
else
{
b = '1' + i - 2;
}
c[x][y] = b;
}
i++;
}

for(y = 0; y<N; y++)
{
for(x = 0; x<N; x++)
{
cout<<c[x][y];
}
cout<<endl;
}
return 0;
}

[/CODE]
#82
HJin2007-06-24 07:40
To Kai:

Kai's post for #3

int N = 15;

should be

const int N = 15;

Otherwise, you need to dynmically alloate the 2d array.

#83
HJin2007-06-24 08:09

/*---------------------------------------------------------------------------
File name: C76-19.cpp
Author: HJin
Created on: 6/19/2007 22:53:55

6/20/2007 11:43:57
-----------------------------------------------------------
Add the soln for fractional Knapsack problem and some comments
about the algorithms.


6/19/2007 23:49:34
-----------------------------------------------------------
Add the soln for 0-1 Knapsack problem and move the 2d memory
functions from my namespace directly into this file.


C76-19.cpp --- the cpp source file for solving the 19th of the 76
classical C/C++ problems.

经典 76 道编程题 之 19:

19. (背包问题) 有 N 件物品 d1,......dN,每件物品重量为 W1,..., WN
(Wi > 0), 每件物品价值为 V1,......VN (Vi>0)。用这N件物品的某个子集
填空背包,使得所取物品的总重量<=TOTAL,并设法使得背包中物品的价值尽可
能高。


Analysis:

In English, this is called the Knapsack problem, in which you are helping
a thief to maximize his loot from a store.

There are two versions of this problem: the 0-1 Knapsack problem and the
fractional Knapsack problem. In the 0-1 Knapsack problem, an item is either
taken or discarded --- you cannot take 0.1 part of an item. In the fractional
Knapsack problem, items can be divided into fractions, say, 0.5.

In general, the the 0-1 Knapsack problem can be solved by a dynamic programming
algorithm, whereas the fractional Knapsack problem can be solved by a greedy
algorithm.

The dynamic programming algorithm needs to first establish a table c[0..n, 0..W],
where n is the number of items, W is the max weight the knapscak can hold.
c[i, w] stands for the max-value for i items and w pounds --- we need to find
c[n, W]. By a math analysis:

c[i, w] = 0, if i=0 or w=0
= c[i-1, w], if i>0 and w_i < w
= max(v_i + c[i-1, w-w_i], c[i-1, w]), if i>0 and w_i < w


The greedy algorithm need to first choose the most valuable item ---
the one with highest value per pound, and then the second most valuable,
and so on.

Sample output

~~~~~ Knapsack problem (0-1) ~~~~~
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 6 6 6 6 6 6 6 6 6 6 6 6 6
0 0 7 7 7 7 13 13 13 13 13 13 13 13 13 13 13
0 0 7 8 8 15 15 15 15 21 21 21 21 21 21 21 21
0 0 7 8 8 15 15 16 17 21 24 24 24 24 30 30 30
0 0 7 8 11 15 18 19 19 26 26 27 28 32 35 35 35
0 0 7 8 11 15 20 20 27 28 31 35 38 39 39 46 46

The thief's max-profit loot choices are:
(20, 6) (11, 4) (8, 3) (7, 2)
( loot weight / max weight = 15 / 16 )
loot value is 46
loot weight is 15

~~~~~ Knapsack problem (fractional) ~~~~~
1
1
1
1
0.2
0
1 * 7 + 1 * 20 + 1 * 11 + 1 * 8 + 0.2 * 9 = 47.8
Press any key to continue . . .


Note that I used an array of 2 columns to store v_i and w_i's.
The first column is for the values;
The second column is for the weights.
The reason is that I need to sort the items by decreasing values per pound
for a greedy algorithm.


Reference:
0. Cormen, Introduction to algorithms, 2nd ed, MIT press.
1. 野比, 相当经典的76道编程自虐题分享.无答案 at
https://bbs.bc-cn.net/viewthread.php?tid=147853
*/

#include <iostream>

#include <string>
#include <sstream>
#include <iomanip>
#define DIM(x) sizeof( (x) ) / sizeof( (x)[0] )

#define hj_debug

using namespace std;


/**
Dynamic programming algorithm for solving the 0-1 knapsack problem.

Space complexity is Theta( n W).
Time complexity is Theta( n W).
*/
int Knapsack01(int (*a)[2], int n, int W);

/**
trackback the optimal soln --- recursive version.
*/
void TrackBack01_recursive(int (*a)[2], int** c, int i, int w);

/**
trackback the optimal soln --- loop version.
*/
void TrackBack01_loop(int (*a)[2], int** c, int i, int w);


/**
Greedy algorithm for solving the fractional Knapsack problem.
*/
double KnapsackFractional(int (*a)[2], int n, int W, double* x);

/**
Track back the optimal soln.
*/
void TrackBackFractional(int (*a)[2], double* x, int n, double maxValue);


/**
Insertion sort to sort the array by decreasing value. The most value item
is the one which has the largest value per pound.
*/
void sortDecreasing(int (*a)[2], int n);


/**
auxilliary functions for 2d array dynamic allocation and deallocation.
*/
template <typename T>
inline T** new2d(int row, int col);

template <typename T>
inline T** new2dInit(int row, int col);

template <typename T>
inline T** new2dInit2(int row, int col);

template <typename T>
inline void delete2d(T** arr2d, int row);

template <typename T>
inline T max2(const T&a, const T& b);

int main(int argc, char** argv)
{
// test #1
//int a[][2] = {
// {60, 10},
// {100, 20},
// {120, 30},
//};


int a[][2] = {
{6, 4},
{7, 2},
{8, 3},
{9, 5},
{11, 4},
{20, 6},
};
int W = 16;
double x[DIM(a)];

cout<<"\n~~~~~ Knapsack problem (0-1) ~~~~~\n";
Knapsack01(a, DIM(a), W);

cout<<"\n~~~~~ Knapsack problem (fractional) ~~~~~\n";
KnapsackFractional(a, DIM(a), W, x);

return 0;
}

int Knapsack01(int (*a)[2], int n, int W)
{
int** c = new2dInit2<int>(n+1, W+1);
int w;
int i;
int lootValue;

for(i=1; i<=n; ++i)
{
for(w=1; w<=W; ++w)
{
c[i][w] = (a[i-1][1] <= w ?
max2(a[i-1][0] + c[i-1][ w-a[i-1][1] ],
c[i-1][w]) : c[i-1][w]);
}
}
lootValue = c[n][W];

#ifdef hj_debug
for(i=0; i<=n; ++i)
{
for(w=0; w<=W; ++w)
cout<<setw(4)<<c[i][w];
cout<<endl;
}
cout<<endl;
#endif

TrackBack01_loop(a, c, n, W);
//TrackBack01_recursive(a, c, n, W);

delete2d(c, n+1);
return lootValue;
}

void TrackBack01_recursive(int (*a)[2], int** c, int i, int w)
{
if(i==0)
{
cout<<endl;
}
else
{
if(c[i][w] == c[i-1][w] )
{
TrackBack01_recursive(a, c, i-1, w);
}
else
{
cout<<"("<<a[i-1][0]<<", "<<a[i-1][1]<<") ";
TrackBack01_recursive(a, c, i-1, w-a[i-1][1]);
}
}
}

void TrackBack01_loop(int (*a)[2], int** c, int i, int w)
{
ostringstream oss;
int val=0;
int weight = 0;
int maxWeight = w;

while(i>0 && w>0)
{
--i;
if( c[i+1][w] != c[i][w] )
{
val += a[i][0];
weight += a[i][1];

oss<<"("<<a[i][0]<<", "<<a[i][1]<<") ";
w-=a[i][1];
}
}

cout<<"The thief's max-profit loot choices are:\n";
cout<<oss.str()<<endl;
cout<<"( loot weight / max weight = "<<weight<<" / "<<maxWeight<<" )"<<endl;
cout<<"loot value is "<<val<<endl;
cout<<"loot weight is "<<weight<<endl;
}


double KnapsackFractional(int (*a)[2], int n, int W, double* x)
{
double lootValue=0;
int i;

for (i=0; i<n; ++i)
x[i] = 0;
int w = 0;

sortDecreasing(a, n);

i=0;
while (w < W)
{
if ( w + a[i][1] <= W )
{
x[i] = 1; // take whole item i
w += a[i][1];
lootValue += a[i][0];
}
else
{
// take fractional parts of item i
// this is the last time that we can
// add an item to the knapsack.
x[i] = double(W - w) / a[i][1];
w = W;
lootValue += x[i] *a[i][0];
}
++i;
}

for(i=0; i<n; ++i)
cout<<x[i]<<endl;

TrackBackFractional(a, x, n, lootValue);

return lootValue;
}

void TrackBackFractional(int (*a)[2], double* x, int n, double maxValue)
{
int i=0;

for(i=0; i<n-1; ++i)
{
if(x[i] > 0)
{
cout<< x[i] <<" * " << a[i][0];
}
else
break;

if(x[i+1] == 0)
cout<< " = "<<maxValue;
else
cout<<" + ";
}

if(x[n-1] >0)
cout<<x[n-1] << " * "<< a[n-1][0] << " = " << maxValue;

cout<<endl;
}

void sortDecreasing(int (*a)[2], int n)
{
for(int out = 1; out<n; ++out)
{
int in = out;
int v = a[out][0];
int w = a[out][1];

while( in >0 && double(a[in-1][0]) / a[in-1][1] < double(v)/w )
{
a[in][0] = a[in-1][0];
a[in][1] = a[in-1][1];
--in;
}

a[in][0] = v;
a[in][1] = w;
}
}


template <typename T>
T** new2d(int row, int col)
{
T** arr2d = new T*[row];
for(int i=0; i<row; ++i)
arr2d[i] = new T[col];

return arr2d;
}

template <typename T>
T** new2dInit(int row, int col)
{
T** arr2d = new T*[row];
int i;
int j;

for(i=0; i<row; ++i)
arr2d[i] = new T[col];

for(i=0; i<row; ++i)
for(j=0; j<col; ++j)
arr2d[i][j] = T();

return arr2d;
}

template <typename T>
T** new2dInit2(int row, int col)
{
T** arr2d = new T*[row];
int i;
int j;

for(i=0; i<row; ++i)
arr2d[i] = new T[col];

for(i=0; i<row; ++i)
arr2d[i][0] = T();

for(j=0; j<col; ++j)
arr2d[0][j] = T();

return arr2d;
}

template <typename T>
void delete2d(T** arr2d, int row)
{
for(int i=0; i<row; ++i)
delete [] arr2d[i];

delete [] arr2d;
}


template <typename T>
T max2(const T&a, const T& b)
{
return (a>b ? a: b);
}

#84
kai2007-06-24 08:30
HJin,
你的担心是不必要的.
我给你一段演示代码,你自己一看便明白了:
[CODE]
#include <iostream>
using namespace std;

int main(void)
{
int n;
cin>>n;
char c[n];
for(int i = 0; i<n; i++)
{
c[i] = 'a' + i;
cout<<c[i]<<" ";
}
return 0;
}
[/CODE]

由于考虑到这个N 可以由用户来设定所以我没有将这个N设为常量.
还有想说的是,如果你的程序中可以不用动态开辟空间就不要动态开辟空间, 这是从程序的执行效率考虑.
#85
kai2007-06-24 08:47
HJin,
就你的第4题的考虑, 我说一些我的看法:
我对第4题也思考了一下, 方法是用穷举, 但即便是穷举, 书写代码上还是有技巧的, 此外还需要一个自己的计数器, 凭我的直觉, 如果k 很大的话, 那么那个count 将是很大的, 完全有可能超过 2^32 - 1.
#86
HJin2007-06-24 08:51

还有想说的是,如果你的程序中可以不用动态开辟空间就不要动态开辟空间, 这是从程序的执行效率考虑.

very good idea. Sometimes, I need to work on very large data set, say, an array of size N = 2^21. Then a[N] has to be dynamically allocated.

[CODE]/*---------------------------------------------------------------------------
File name: stack_vs_heap.cpp
Author: HJin
Created on: 6/23/2007 17:47:16
Modification history:
*/
#include <iostream>
using namespace std;
int main(int argc, char** argv)
{
const int N = (1<<20);

// does not work since N is too big for a[N] to be on the program stack
// int a[N];
// Okay --- as long as your heap memory is large enough.
int * b = new int [N];
return 0;
}[/CODE]


#87
HJin2007-06-24 09:03
回复:(kai)HJin,就你的第4题的考虑, 我说一些我的看...
very good observations. As I mentioned in my original post, enumeration (or brute force) approach is applicable only for n<=6. In fact, n=6 takes too long on my PC to output result.

For n = 7, the number of different Latin squares overflows 32-bit integers.

Yes, there is a lot of places in my code that can be optimized.
#88
kai2007-06-24 09:19
HJin,
哈哈, 看来你没有找到合适的方法, 你也没有理解我的关于程序的书写技巧的那句话. 我现在只想告诉你, 不要把没有必要的中间状态带入下一层, 这便是节约内存的根本. 代码我还没有写, 但是凭我的直觉, 这道题如果思考方式不对, 那么是没有可能给出结果的.

我的算法不敢说对任意大的数都有效, 但是k 取个几十, 几百还是可以解的. 由于题目要求输出排列方案, 这自然要影响程序的执行时间, 不过这不会扩展对内存的需要.
#89
aipb20072007-06-24 09:59
以下是引用kai在2007-6-24 8:30:26的发言:
HJin,
你的担心是不必要的.
我给你一段演示代码,你自己一看便明白了:
[CODE]
#include <iostream>
using namespace std;

int main(void)
{
int n;
cin>>n;
char c[n];
for(int i = 0; i<n; i++)
{
c[i] = 'a' + i;
cout<<c[i]<<" ";
}
return 0;
}
[/CODE]

由于考虑到这个N 可以由用户来设定所以我没有将这个N设为常量.
还有想说的是,如果你的程序中可以不用动态开辟空间就不要动态开辟空间, 这是从程序的执行效率考虑.

这里数组的大小不是必须要在编译时为常量吗?kai,你这样可以吗?

#90
kai2007-06-24 10:22
aipb2007,
你何必问我呢? 你为什么不自己试一下呢? 我既然把代码给出了, 想必不会是假的, 你说是不是?
#91
aipb20072007-06-24 10:26
第4题我觉得可以用递归回朔的方法。
这样比穷举计算次数要少些,但是因为递归会使效率下降。

所以综合起来就不知道哪个优了!
#92
aipb20072007-06-24 10:32
以下是引用kai在2007-6-24 10:22:02的发言:
aipb2007,
你何必问我呢? 你为什么不自己试一下呢? 我既然把代码给出了, 想必不会是假的, 你说是不是?

我试了啊,报错了,我理解的:静态数组大小只能是常量。

但是确实在某些编译器上允许这样。
我机器上有3个,所以就测试了3次,dev-cpp中可以,vc不可以。

所以我疑惑。根据我目前所了解,可以设置大小只能用动态数组!

#93
aipb20072007-06-24 10:57
以下是引用kai在2007-6-24 8:47:10的发言:
HJin,
就你的第4题的考虑, 我说一些我的看法:
我对第4题也思考了一下, 方法是用穷举, 但即便是穷举, 书写代码上还是有技巧的, 此外还需要一个自己的计数器, 凭我的直觉, 如果k 很大的话, 那么那个count 将是很大的, 完全有可能超过 2^32 - 1.

我大致看了下HJin对这个问题的解法,我如果写,我也会有个同样的问题,不仅这个问题,一切有关类似这样的递归回溯的问题都存在,比如皇后,比如我在46楼写那个题。

我很想看看kai你是怎么处理当n很大时的情况。当然,不要让程序一直运行个几天才算出来,呵呵~(我想的话)。

#94
laigaoat20052007-06-24 11:56

第三题:(算法来自第69楼)

#include "iostream.h"
#include "stdio.h"
#include "iostream.h"
#include "conio.h"
main()
{
char ch[11]={'T','J','1','2','3','4','5','6','7','8','9'};
char output[21][21];
int i,j,m=0;
int x=1;
while (x)
{
while (!((m>=3&&m<=21)))
{
step: printf("输入一个3到20之间的数:");
scanf("%d",&m);
}


for(i=m;i>=0;i--)
{
for(j=i;j<=m-i;j++)
{
output[i][j]=ch[i];
output[j][i]=ch[i];
output[m-i][m-j]=ch[i];
output[m-j][m-i]=ch[i];
}
}
for (i=0;i<m;i++)
{
for (j=0;j<m;j++)
{
cout<<output[i][j];
}
cout<<"\n";
}
cout<<"输入任意数字继续,输入0退出!";
scanf("%d",&x);
if (x!=0) goto step;
}
}

#95
kai2007-06-24 12:18
aipb2007,
对于第四题,我个人认为用递归不可行,只能用iterative 的方法. 递归的一个根本的弱点是对内存空间的不必要的开销,因为递归的多层运算的顺利进行是建立在这么一个基础上,即多层次的中间状态需要保留以便回溯计算.而反观iterative 的方法就不是这样, 某个中间状态可以用一个变量来保留,当进入第2层的时候, 第一层已经不存在,其运算结果由某个变量来保留以便下一层的计算需要,当第二层计算完毕后,变量予以更新. 当进入再下一层的时候,只有那个变量被传递到下一层,所以内存开销被节约了.

lisp 和 scheme 都是建立在recursive 这样一个运算方法上的编程语言, 其基本数据结构就是list, 一个list 可以看成 first 和 rest 的一个组成. 那么一个对于一个list 可行的递归算法就是 f(list) = g(first, f(rest)); 我非常反感lisp 这种限制自由思想的编程语言. 但我也承认递归思想是非常有用的一种思想.

#96
I喜欢c2007-06-24 12:35
恩...

学习了..
#97
aipb20072007-06-24 12:40
以下是引用kai在2007-6-24 12:18:53的发言:
aipb2007,
对于第四题,我个人认为用递归不可行,只能用iterative 的方法. 递归的一个根本的弱点是对内存空间的不必要的开销,因为递归的多层运算的顺利进行是建立在这么一个基础上,即多层次的中间状态需要保留以便回溯计算.而反观iterative 的方法就不是这样, 某个中间状态可以用一个变量来保留,当进入第2层的时候, 第一层已经不存在,其运算结果由某个变量来保留以便下一层的计算需要,当第二层计算完毕后,变量予以更新. 当进入再下一层的时候,只有那个变量被传递到下一层,所以内存开销被节约了.

lisp 和 scheme 都是建立在recursive 这样一个运算方法上的编程语言, 其基本数据结构就是list, 一个list 可以看成 first 和 rest 的一个组成. 那么一个对于一个list 可行的递归算法就是 f(list) = g(first, f(rest)); 我非常反感lisp 这种限制自由思想的编程语言. 但我也承认递归思想是非常有用的一种思想.

说的很有道理,但是递归对内存的开销并不都是不必要啊。难道所有的递归算法都可以被迭代取代而不用到栈?
期待你的solution,这样说不好懂

呵呵

#98
kai2007-06-24 14:52
aipb2007,
代码在书写中, 已经写了一个class MyCounter, 这个class 很简单, 用一个char array 来完成对递增的信息存储. 使用的是100进制, 为的是输出的方便和节约内存空间.
下一个正在写的class 就是 LatinMatrix, 我的算法是这样的. 首先根据N 的大小, 初始一个string array 并统统赋初值: 1234...N, 通过一个check 函数来判断属于LatinMatrix 的那些string array 的组合. 判断的方法是这样的. 在第一个str不动的情况下, 通过 next_permutation(...); 变动第二个str, 然后调用我事先写好的一个函数findSameChar(...); 如果没有发现相同的字符,那么还是
通过 next_permutation(...); 变动第三个str, 然后还是调用findSameChar(...); 来判断在纵向所新形成的字符串中有没有相同的字符, 如果还是没有那么继续下一层, 如果发现相同, 那么当前这一层的运算退出, 回到上一层.

next_permutation 有一个非常方便的地方,那就是一个不拉的, 一个不重复的展现所有可能.
#99
jiushiwo2007-06-25 11:12
不错,是个值得学习的地方
#100
kai2007-06-25 12:38
我要睡觉了,今天还有自己的事,先将局部代码传上来,大家可以看看是否对你的开发有所启发:
[CODE]
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

class MyCounter
{
private:
enum{N = 10800};
char counter[N];
public:
MyCounter()
{
reset();
}
void reset()
{
for(int i = 0; i<N; i++)
{
counter[i] = 0;
}
}
void display()
{
int i = 0;
while(i<N && (int)counter[i]==0)
{
i++;
}
int first = i;
if(first == N)
cout<<"0";
else
cout<<(int)counter[first];
for(i = first+1 ; i<N; i++)
{
int n = (int)counter[i];

if(n == 0)
cout<<"00";
else if(n<10)
cout<<'0'<<n;
else
cout<<n;
}
}
void operator++()
{
for(int i = N-1; i>=0; i--)
{
int num = (int)counter[i];
if(num<99)
{
counter[i] = ++num;
break;
}
else
{
counter[i] = 0;
continue;
}
}
}
};
class LatinMatrix
{
private:
int n; // to indicate how many numbers we have
string * lmStr; // an string array to save all the init numbers
vector<string *>strPerm; // to save the all combination of numbers
int * pos; // pos[0] indicates the current position of the first line,
// pos[1] indicates the current position of the second line, and so on
MyCounter myCounter; // to save the count of LatinMatrix
public:
static bool findSameChar(const char * t)
{
string temp;
int length = strlen(t);
for(int i = 0; i<length; i++)
temp += t[i];
for(i = 0; i<length; i++)
{
int pos = temp.find(t[i]);
if(pos != i)
return true;
}
return false;
}
static void integerToString(string & str, const int & t)
{
ostringstream oss;
oss << t;
str = oss.str();
}
LatinMatrix(int n)
{
this->n = n;
lmStr = new string[n];
pos = new int[n];
string temp;
for(int i = 0; i<n; i++)
{
pos[i] = 0; // init all line's current position to 0
integerToString(temp, i+1);
lmStr[i] = string(temp);
}
sort(lmStr, lmStr+n);
strPerm.push_back(lmStr);
while(next_permutation(lmStr, lmStr+n))
{
string * newLmStr = new string[n];

for(int j = 0; j<n; j++)
{
newLmStr[j] = lmStr[j];
}
strPerm.push_back(newLmStr);
}
}
void trytoFindLatinMatrix() // when I find a LatinMatrix, I print the matrix and increment the count
{
// the method to find the LatinMatrix is:
// first I get the first string array
// and then I get the second string array
// and then I check, whether this second string array is valid,
// when yes, then go with the third string array
// otherwise, I go a step back,
// something must be updated within this process, this is the pos value
// pos records the value of string array of permutation,
// so that I can follow the trace of the process

}
~LatinMatrix()
{
delete [] lmStr;
delete [] pos;
}
void display()
{
for(int i = 0; i<strPerm.size(); i++)
{
string * pStr = strPerm[i];
for(int j = 0; j<n; j++)
{
cout<<pStr[j]<<" ";
}
cout<<endl;
}
}
};
int main()
{
LatinMatrix lm(5);
lm.display();
return 0;
}
[/CODE]
#101
aipb20072007-06-25 15:53

也有事,空了研究。
UP
123456