征求一算法. (看来都嫌分少,这样吧再加300,一共400了) (100分)

  • 主题发起人 主题发起人 LeeChange
  • 开始时间 开始时间
可以保证。当n>v时,如上公式成立,设步数为x
当n<v时,如果(v-n) mod 2 = 0 ,则步数为x+2,否则步数为x+3
就是这个样子了:)
 
完成了一半:)
——在N>=2*M时,可以使用非常简单的计算方法得到最优结果(不用任何试探性的搜索) :-)
function TurnCoins(N,A,M:Integer):Integer;
procedure Output(B,NewA:Integer);
var
Str:String;
begin
if B>=0 then
begin
Inc(Result);
Str:=Format('第 %2d 步:'#13#10'翻动: 正面 %8d 反面 %8d',[Result,B,M-B]);
end
else
Str:='';
Str:=Str+Format(#13#10'现有: 正面 %8d 反面 %8d',[NewA,N-NewA]);
frmMain.Memo1.Lines.Add(Str);
end;
procedure Turn(B:Integer);
begin
Dec(A,2*B-M);
Output(B,A);
end;
begin
Result:=-1;
Output(-1,A);
if (A and $01=1) and (M and $01=0) then
//A奇 M偶——不可能成功
exit;
Result:=0;
if M<=N div 2 then
//N>=M*2 ——不用考虑 N-B 是否合法
begin
while A>0do
begin
if (A>=2*M) or (A=M) then
Turn(M)
else
//let A'=M ——核心!? 如此简单 :P
Turn(A div 2)
end;
end
else
begin
//未完成...
end;
end;

continue working...
 
to 叶侠:
还是写程序吧,好测试一些.
to creation-zy:
现在手头没有环境,也没有程序,不好对照测你的前半部分,明天一早就测.
期待你完成后半部分.
 
个人认为用双向广度比较好(当然,真能找出数学方法就最好了)
 
to AI_Player:
此题的目标状态不明确,不适合用双向广度的。
其实我把输入数据规模限制在255以内,就是想解的时候可以用最一般的广度优先。呵呵。
如果叶兄能找出数学规律,则效率肯定非用搜索的程序能比。
 
搞定了!!!!!!!!!!!!!!!!!!!!!:))))
var
Path: string;
function GetMinPP(m, n, v: Integer): Integer;
var
Tmp: Integer;
procedure AddPath(P: Integer);
begin
Path := Path + IntToStr(P) + ',';
end;
begin
if n > m then
// 翻有最小解的那一堆
begin
n := n + m;
m := n - m;
n := n - m;
end;

Result := -1;
if (n mod 2 = 1) and (v mod 2 = 0) or (m + n <= v) or (m*n*v <= 0) then
//无解情况返回-1
Exit;
if n mod v = 0 then
//正好整除就很简单了
begin
Result := n div v;
end
else
begin

{ 非整除情况 }
Tmp := n mod v;
//Tmp 是小于v的余数: n mod v
if ((v-Tmp) mod 2 = 0) and (n > v) then
//对于n>v时,tmp有可能直接变成0,这种情况比较特殊
begin
AddPath((Tmp + v) div 2);
Result := 1;
end
else
begin
if m < v - Tmp div 2 then
Exit;
// 本来m要翻至少v-tmp div 2 个子的,但如果m不够数,表示没有翻动的余地了,无解。
Result := 2;
if Tmp = 1 then
//tmp等于1是个很奇怪的情况,如果移n,至少需要三步
begin
if (m mod v = 0) and (m div v < 3) then
//如果移m少于三步,那移m好了
begin
Path := '';
AddPath(v);
if m div v = 2 then
AddPath(v);
Result := m div v;
Exit;
end;
AddPath(1);
//把n变成不是1,再按照一般的解法解。
Tmp := v - 1;
Inc(Result);
end;

{ 这一段是核心原理 }
if Tmp mod 2 = 0 then
// Tmp mod 2=0 表示可以只一次组合成v倍数
begin
AddPath(Tmp div 2);
AddPath(v);
//对于n<v的情况,tmp无法直接变成0,只好往上发展了,因此路径加v
end
else
if Tmp = 1 then
// 1的情况也比较特殊,可以直接减1
AddPath(v div 2 + 1)
else
begin
AddPath(v div 2);
//非1情况先把n变为偶数
AddPath(Tmp div 2 + 1);
//变成偶数就可由v-2i一次性组合成功。
end;
end;
end;

while n >= vdo
//加入大于v的部分,完成!
begin
AddPath(v);
Dec(n, v);
Inc(Result);
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
m, n, v: Integer;
begin
m := StrToInt(Edit1.Text);
//m = 正面为上
n := StrToInt(Edit2.Text);
//n = 反面为上
v := StrToInt(Edit3.Text);
//v = 翻动个数
Path := '';
Button1.Caption := IntToStr(GetMinPP(m, n, v));
Edit4.Text := Path;
end;
 
to 叶不归:
测试了您的程序.
m=5, n=5, v=5时, Button1的Caption怎么等于2?
m=11, n=12, v=10时,您的程序好象无解.实际是有解的.
 
不好意思:)漏了:
对于5,5,5,在程序最后又执行了while所以又加了一次。
对于12,11,10,只考虑了11 和10便判断无解了,其实还应该判断m是否是偶数
修改如下:
var
Path: string;
function GetMinPP(m, n, v: Integer): Integer;
var
Tmp: Integer;
procedure AddPath(P: Integer);
begin
Path := Path + IntToStr(P) + ',';
end;
begin
if n > m then
// 翻有最小解的那一堆
begin
n := n + m;
m := n - m;
n := n - m;
end;

Result := -1;
if (n mod 2 = 1) and (v mod 2 = 0) or (m + n <= v) or (m*n*v <= 0) then
//无解情况返回-1
if m mod 2 = 0 then
begin
n := n + m;
m := n - m;
n := n - m;
end
else
Exit;
if n mod v = 0 then
//正好整除就很简单了
begin
Result := n div v - 1;
end
else
begin

{ 非整除情况 }
Tmp := n mod v;
//Tmp 是小于v的余数: n mod v
if ((v-Tmp) mod 2 = 0) and (n > v) then
//对于n>v时,tmp有可能直接变成0,这种情况比较特殊
begin
AddPath((Tmp + v) div 2);
Result := 1;
end
else
begin
if m < v - Tmp div 2 then
Exit;
// 本来m要翻至少v-tmp div 2 个子的,但如果m不够数,表示没有翻动的余地了,无解。
Result := 2;
if Tmp = 1 then
//tmp等于1是个很奇怪的情况,如果移n,至少需要三步
begin
if (m mod v = 0) and (m div v < 3) then
//如果移m少于三步,那移m好了
begin
Path := '';
AddPath(v);
if m div v = 2 then
AddPath(v);
Result := m div v;
Exit;
end;
AddPath(1);
//把n变成不是1,再按照一般的解法解。
Tmp := v - 1;
Inc(Result);
end;

{ 这一段是核心原理 }
if Tmp mod 2 = 0 then
// Tmp mod 2=0 表示可以只一次组合成v倍数
begin
AddPath(Tmp div 2);
AddPath(v);
//对于n<v的情况,tmp无法直接变成0,只好往上发展了,因此路径加v
end
else
if Tmp = 1 then
// 1的情况也比较特殊,可以直接减1
AddPath(v div 2 + 1)
else
begin
AddPath(v div 2);
//非1情况先把n变为偶数
AddPath(Tmp div 2 + 1);
//变成偶数就可由v-2i一次性组合成功。
end;
end;
end;

while n >= vdo
//加入大于v的部分,完成!
begin
AddPath(v);
Dec(n, v);
Inc(Result);
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
m, n, v: Integer;
begin
m := StrToInt(Edit1.Text);
//m = 正面为上
n := StrToInt(Edit2.Text);
//n = 反面为上
v := StrToInt(Edit3.Text);
//v = 翻动个数
Path := '';
Button1.Caption := IntToStr(GetMinPP(m, n, v));
Edit4.Text := Path;
end;
 
to 叶不归:
m=9, n=9, v=17您的程序说无解,实际是有解的.
 
9,9,17的解是什么?
 
9步:8,10,6,12,4,14,2,16,0
 
faint.........
看来要在以下这段做文章了:(
if m < v - Tmp div 2 then
Exit;
// 本来m要翻至少v-tmp div 2 个子的,但如果m不够数,表示没有翻动的余地了,无解
......
您自已写写看?
 
我的已经写好了,不过走的不是您的路子,我相信,如果您通过数学方法实现,效率肯定比我的高的多.
 
能把代码贴出来吗?
 
私下给你吧,因为我还是希望有人拿走这400分.
 
谢谢,soft-sword@163.com
 
又是一个数论的问题,如果想精确求最优解,与其说是一个计算机问题还不如说是一个数学问题。
因为是求最少步骤,所以不得不穷举每一可能的情况.[:(]
 
to DarwinZhang:
呵呵,还不至于要用穷举法吧.
另外,我非常希望能用数学的方法归纳出来,可惜我数学太差,所以只有拿计算机做了.
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
D
回复
0
查看
2K
DelphiTeacher的专栏
D
D
回复
0
查看
2K
DelphiTeacher的专栏
D
D
回复
0
查看
1K
DelphiTeacher的专栏
D
后退
顶部