By satan ( 十月 24, 2010 at 3:58 下午) · Filed under 算法, 算法
看到这样的一个题:
从1到100000中任意拿掉两个数字,把剩下99998个数的顺序打乱并且放到数组中,要求只扫描一遍,把这个两个数找出来,可以使用最多不超过5个局部变量,不能使用数组变量并且不能改变数组的值。
刚看到这个题, 瞬间想到, 我们遍历1-100000,不就能把这两个数找出来了么,再看看不对呀,是把剩下的99998 个数乱序放在数组中的, 还不能修改数组的值。这可麻烦了。
那既然是乱序的,那就不能从单个数上考虑了, 也许可以从整体上考虑吧。
只是缺了两个数, 用原数组的和减去新的数组的和不就是这两个数的和了。
a+b=Q (1)
哈哈, 一个二元一次方程, 还差一个方程。
跟上面的思路一样, 用原数组的乘积除于新的数组的乘积就是这两个数的乘积
a * b = P (2)
这可麻烦了, 阶乘是多大的数呀, 程序那不溢出了,那拆成子项试试
i/arr[i] * (i+1) /arr[i+1] * …
实验了一下, 从1开始的话, 这个数就变0 了,
从999998 开始, 这个数就无穷大了。
这个头大了, 进行不下去了。
n 个小时后……
还是得会到加号上来, 我可以先平方嘛。哈哈, 对, 先平方后一减, 可得
a^2 + b^2 = P (3)
(1)(3)方程联合, 立马可以解得了。
Permalink
By satan ( 九月 29, 2010 at 10:40 上午) · Filed under 算法, 算法
问题是这样的:
有三个瓶子, 分别可以装 12L, 8L和5L 的酒, 现在12 L 的瓶子装满了酒,他们都没有刻度, 现在要用这几个瓶子倒出两个6L 的酒来。
用人来做的话还是不难的, 用机器来做就要把这个问题抽象一下了。先上代码吧, 至于思路有放假回来再慢慢整。
#include
using namespace std;
//三个瓶子以0,1,2标识
/*int V[3]={12,8,5};//三个瓶子的容积*/
int V[3]={8,5,3};//三个瓶子的容积
int objnum = 3;
//可能的倒酒方式有6种,从src[i]到dest[i]
int src[6] ={0,0,1,1,2,2};
int dest[6]={1,2,0,2,0,1};
int record[100][3];//record[i][0~2]记录三个瓶子盛酒的状态
int rec_index=0;//已知的状态数
//从a瓶倒酒到b瓶
void Pour(int state[],int a,int b)
{
// 还剩这么多可以倒过来
int r=V[b]-state[b];
// 可以倒得比还剩的少, 把被倒杯的都倒过来
if(state[a]
Permalink
By satan ( 九月 29, 2010 at 8:49 上午) · Filed under 算法, 算法
在一个8 × 8 的国际象棋棋盘上, 放8 个皇后, 让她们彼此不能吃到对方(皇后的走法是可以吃到直线包括横线上的棋子),下面是一个合法的例子:
5 0 0 0 0 0 0 0
0 0 0 0 5 0 0 0
0 0 0 0 0 0 0 5
0 0 0 0 0 5 0 0
0 0 5 0 0 0 0 0
0 0 0 0 0 0 5 0
0 5 0 0 0 0 0 0
0 0 0 5 0 0 0 0
这个问题的解法还是比较简单的, 逐行放置合法的棋子, 直到8个合法的棋子都放完。
基于这个, 很容易就想到一个递归的解法,在当前已有的选择中找一个合法的位置。
当然递归的都可以化为非递归的, 当然要自己模拟栈的情况了。
递归算法
递归算法有几个重要的点:进入递归的条件, 递归退出的条件,退栈的时候要处理什么
这里是这个问题相对应的:
进入递归的条件:找到了一个合法的位置 valid(tmp)
递归退出的条件: 找到了一组合法的组合或者当前路径无法找到合法的组合 rec_index == 8
退栈的时候要处理什么: 把找到的合法个数减一 rec_index–;
#include
using namespace std;
// 保存结果
int record[8][2];
// 保存已经选中了的元素的个数
int rec_index = 0;
// 验证当前行是否合法
bool valid(int a[]) {
for (int i = 0; i < rec_index; i++) {
int p0 = a[0] - record[i][0];
int p1 = a[1] - record[i][1];
// 不能在一条线上, 横竖
if((p0 == 0) || (p1==0) || (p0 == p1) || (p0 == -p1)) {
return false;
}
}
return true;
}
void CopyArr(int ori[], int dest[], int length) {
for ( int i = 0; i < length; i++) {
dest[i] = ori[i];
}
}
// 记录选择元素
void Record(int a[]) {
CopyArr(a, record[rec_index], 2);
}
void Output()
{
for(int i=0;i
非递归版本
非递归要自己处理退栈 popstack(i, q), 在找到了一组合法的组合或者当前路径无法找到合法的组合的时候
#include
using namespace std;
// 保存结果
int record[8][2];
// 保存已经选中了的元素的个数
int rec_index = 0;
// 验证当前行是否合法
bool valid(int a[]) {
for (int i = 0; i < rec_index; i++) {
int p0 = a[0] - record[i][0];
int p1 = a[1] - record[i][1];
// 不能在一条线上, 横竖
if((p0 == 0) || (p1==0) || (p0 == p1) || (p0 == -p1)) {
return false;
}
}
return true;
}
void CopyArr(int ori[], int dest[], int length) {
for ( int i = 0; i < length; i++) {
dest[i] = ori[i];
}
}
// 记录选择元素
void Record(int a[]) {
CopyArr(a, record[rec_index], 2);
}
void Output()
{
for(int i=0;i=0) {
bool found = false;
for (int j = q; j < 8; j++) {
// 记录当前状态
int tmp[] = {i,j};
// 检查当前元素是否是合法的
if(valid(tmp)) {
// 当前元素合法, 记录它
Record(tmp);
i++;
found = true;
// 这一行找到了, 到下一行的开始去找
q = 0;
rec_index++;
break;
}
}
// 退栈操作
if(!found) {
popstack(i, q);
}
// 找到一组答案
if(found && (rec_index == 8)) {
Output();
// 只找一个结果
if(!all) {
break;
} else {
// 找出所有的结果
popstack(i, q);
}
}
}
return false;
}
int main() {
Solve(true);
return 0;
}
Permalink