任意次序的n个烙饼最小反转次数求解 暑期学习笔记(十)
贴上详细的题目:
星期五的晚上,一帮微软技术员在希格玛附近的“硬盘酒吧”谈算法问题。有个同事说: 我以前在烙饼店打工,顾客经常端非常多的烙饼。店里的饼大小不一, 我习惯在到达顾客饭桌前,把一摞饼按照大小次序摆好——小的在上面,大的在下面。由于我一只手托着盘子,只好用另一只手,一次抓住最上面的几块饼,把它们 上下颠倒个个儿,反复几次之后,这摞烙饼就排好序了。
假设有n块大小不一的烙饼,那最多/最少要翻几次,才能达到最后大小有序的结果呢? 吧台的酒保说:这太难了吧。我有个简单的问题。有一次我烙了三个饼,一个两面都焦了,一
个两面都是金黄色,一个一面是焦的,一面是金黄色,我把它们摞一起,只能看到最上面一个饼的一面,发现是焦的,问最上面这个饼的另一面是焦的概率是多少? 不少喝酒的人脱口而出:1/2! 上面的说法对吗?你能否写出一个程序,对于n块大小不一的烙饼,输出最优化的翻饼过程呢?
Code
usings#region usings
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Generic;
#endregion
namespace CakeSorting
{
class Program
{
Vars#region Vars
private int[] cakeSizeArray;//大饼直径队列
private int[] cakeArray;//大饼队列
private Stack<int> steps;
private Queue<int> cakes;
const int cakeNum = 7;
private int maxTime = 0;
private int minTime = 0;
#endregion
static void Main(string[] args)
{
Program p = new Program();
p.run();
}
void run()
{
DateTime dt = DateTime.Now;
TimeSpan ts;
init();
enumeratQueue(cakes.Count());
ts = DateTime.Now - dt;
Console.WriteLine("\nResult\nMinTime is:{0}\t{1}:{2}:{3}:{4}", minTime, ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds);
Console.ReadLine();
}
void init()
{
//初始化赋值
setMaxTime();
minTime = 0;
cakeArray = new int[cakeNum];
cakeSizeArray = new int[cakeNum];
cakes = new Queue<int>();
steps = new Stack<int>();
for (int i = 0; i < cakeSizeArray.Length; i++)
{
cakeSizeArray[i] = i;
}//大饼直径初始化
foreach (int i in cakeSizeArray)
{
cakes.Enqueue(i);
}//大饼队列化
}
void enumeratQueue(int cn)
{
if (cn == 1)
{
int t = cakes.Dequeue();
cakeArray[0] = t;
setMaxTime();
search(0);
minTime = maxTime > minTime ? maxTime : minTime;
show();
cakes.Enqueue(t);
}
for (int i = 0; i < cn; i++)
{
int t = cakes.Dequeue();
cakeArray[cn - 1] = t;
enumeratQueue(cn - 1);
cakes.Enqueue(t);
}
}
void search(int step)
{
if (maxTime < 15 * cakeNum / 14)
return;//当此最优解必小于此最劣情况下界时剪枝
if (step > maxTime)
return;//大于2(n-1)时退出
if (step + getLowerTime() > maxTime)
return;//最优预计
for (int i = 1; cakeArray [i-1]<cakeArray [i]; i++)
{
if (i == cakeArray.Length - 1)
{
maxTime = step;
return;
}
}//排序成功退出
for (int i = 0; i < cakeArray.Length; i++)
{
revert(0, i);
search(step+1);
revert(0, i);
}//递归穷举所有方案
}
void revert(int begin, int end)
{
int t = cakeArray[begin];
for (int i = begin, j = end; i < j; i++, j--)
{
t = cakeArray[i];
cakeArray[i] = cakeArray[j];
cakeArray[j] = t;
}
}
void show()
{
foreach (int i in cakeArray)
{
Console.Write("{0}\t", i);
}
Console.Write("\nMaxTime is:{0}\tMinTime is:{1}\n**************************************************\n",maxTime ,minTime);
}
void setMaxTime()
{
//maxTime = 2 * (cakeNum - 1);
maxTime = (5 * cakeNum + 5) / 3 < 2 * (cakeNum - 1) ? (5 * cakeNum + 5) / 3 : 2 * (cakeNum - 1);
}
int getLowerTime()
{
int count=0;
for (int i = 1; i < cakeNum; i++)
{
if (Math.Abs(cakeArray[i - 1] - cakeArray[i]) != 1)
count++;
}
return count;
}
}
}
usings#region usings
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Generic;
#endregion
namespace CakeSorting
{
class Program
{
Vars#region Vars
private int[] cakeSizeArray;//大饼直径队列
private int[] cakeArray;//大饼队列
private Stack<int> steps;
private Queue<int> cakes;
const int cakeNum = 7;
private int maxTime = 0;
private int minTime = 0;
#endregion
static void Main(string[] args)
{
Program p = new Program();
p.run();
}
void run()
{
DateTime dt = DateTime.Now;
TimeSpan ts;
init();
enumeratQueue(cakes.Count());
ts = DateTime.Now - dt;
Console.WriteLine("\nResult\nMinTime is:{0}\t{1}:{2}:{3}:{4}", minTime, ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds);
Console.ReadLine();
}
void init()
{
//初始化赋值
setMaxTime();
minTime = 0;
cakeArray = new int[cakeNum];
cakeSizeArray = new int[cakeNum];
cakes = new Queue<int>();
steps = new Stack<int>();
for (int i = 0; i < cakeSizeArray.Length; i++)
{
cakeSizeArray[i] = i;
}//大饼直径初始化
foreach (int i in cakeSizeArray)
{
cakes.Enqueue(i);
}//大饼队列化
}
void enumeratQueue(int cn)
{
if (cn == 1)
{
int t = cakes.Dequeue();
cakeArray[0] = t;
setMaxTime();
search(0);
minTime = maxTime > minTime ? maxTime : minTime;
show();
cakes.Enqueue(t);
}
for (int i = 0; i < cn; i++)
{
int t = cakes.Dequeue();
cakeArray[cn - 1] = t;
enumeratQueue(cn - 1);
cakes.Enqueue(t);
}
}
void search(int step)
{
if (maxTime < 15 * cakeNum / 14)
return;//当此最优解必小于此最劣情况下界时剪枝
if (step > maxTime)
return;//大于2(n-1)时退出
if (step + getLowerTime() > maxTime)
return;//最优预计
for (int i = 1; cakeArray [i-1]<cakeArray [i]; i++)
{
if (i == cakeArray.Length - 1)
{
maxTime = step;
return;
}
}//排序成功退出
for (int i = 0; i < cakeArray.Length; i++)
{
revert(0, i);
search(step+1);
revert(0, i);
}//递归穷举所有方案
}
void revert(int begin, int end)
{
int t = cakeArray[begin];
for (int i = begin, j = end; i < j; i++, j--)
{
t = cakeArray[i];
cakeArray[i] = cakeArray[j];
cakeArray[j] = t;
}
}
void show()
{
foreach (int i in cakeArray)
{
Console.Write("{0}\t", i);
}
Console.Write("\nMaxTime is:{0}\tMinTime is:{1}\n**************************************************\n",maxTime ,minTime);
}
void setMaxTime()
{
//maxTime = 2 * (cakeNum - 1);
maxTime = (5 * cakeNum + 5) / 3 < 2 * (cakeNum - 1) ? (5 * cakeNum + 5) / 3 : 2 * (cakeNum - 1);
}
int getLowerTime()
{
int count=0;
for (int i = 1; i < cakeNum; i++)
{
if (Math.Abs(cakeArray[i - 1] - cakeArray[i]) != 1)
count++;
}
return count;
}
}
}
上一篇写了当已知一个烙饼队列,求出其最少反转次数。
这一篇中将求出如题即n个烙饼最坏情况下需要多少次反转。
《编程之美》中给出目前最优最大下界:15n/14,最小的上界(5n+5)/3。
c#代码:
Code
usings#region usings
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Generic;
#endregion
namespace CakeSorting
{
class Program
{
private int[] cakeSizeArray;//大饼直径队列
private int[] cakeArray;//大饼队列
private Queue<int> cakes;
const int cakeNum = 6;
private int maxTime = 0;
private int minTime = 0;
static void Main(string[] args)
{
Program p = new Program();
p.run();
}
void run()
{
init();
enumeratQueue(cakes.Count());
Console.ReadLine();
}
void init()
{
//初始化赋值
setMaxTime();
minTime = 0;
cakeArray = new int[cakeNum];
cakeSizeArray = new int[cakeNum];
cakes = new Queue<int>();
for (int i = 0; i < cakeSizeArray.Length; i++)
{
cakeSizeArray[i] = i;
}//大饼直径初始化
foreach (int i in cakeSizeArray)
{
cakes.Enqueue(i);
}//大饼队列化
}
void enumeratQueue(int cn)
{
if (cn == 1)
{
int t = cakes.Dequeue();
cakeArray[0] = t;
setMaxTime();
search(0);
minTime = maxTime > minTime ? maxTime : minTime;
show();
cakes.Enqueue(t);
}
for (int i = 0; i < cn; i++)
{
int t = cakes.Dequeue();
cakeArray[cn - 1] = t;
enumeratQueue(cn - 1);
cakes.Enqueue(t);
}
}
void search(int step)
{
if (step > maxTime)
return;//大于2(n-1)时退出
for (int i = 1; cakeArray [i-1]<cakeArray [i]; i++)
{
if (i == cakeArray.Length - 1)
{
maxTime = step;
return;
}
}//排序成功退出
for (int i = 0; i < cakeArray.Length; i++)
{
revert(0, i);
search(step+1);
revert(0, i);
}//递归穷举所有方案
}
void revert(int begin, int end)
{
int t = cakeArray[begin];
for (int i = begin, j = end; i < j; i++, j--)
{
t = cakeArray[i];
cakeArray[i] = cakeArray[j];
cakeArray[j] = t;
}
}
void show()
{
foreach (int i in cakeArray)
{
Console.Write("{0}\t", i);
}
Console.Write("\nMaxTime is:{0}\tMinTime is:{1}\n",maxTime ,minTime);
}
void setMaxTime()
{
maxTime = 2 * (cakeNum - 1);
}
}
}
usings#region usings
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Generic;
#endregion
namespace CakeSorting
{
class Program
{
private int[] cakeSizeArray;//大饼直径队列
private int[] cakeArray;//大饼队列
private Queue<int> cakes;
const int cakeNum = 6;
private int maxTime = 0;
private int minTime = 0;
static void Main(string[] args)
{
Program p = new Program();
p.run();
}
void run()
{
init();
enumeratQueue(cakes.Count());
Console.ReadLine();
}
void init()
{
//初始化赋值
setMaxTime();
minTime = 0;
cakeArray = new int[cakeNum];
cakeSizeArray = new int[cakeNum];
cakes = new Queue<int>();
for (int i = 0; i < cakeSizeArray.Length; i++)
{
cakeSizeArray[i] = i;
}//大饼直径初始化
foreach (int i in cakeSizeArray)
{
cakes.Enqueue(i);
}//大饼队列化
}
void enumeratQueue(int cn)
{
if (cn == 1)
{
int t = cakes.Dequeue();
cakeArray[0] = t;
setMaxTime();
search(0);
minTime = maxTime > minTime ? maxTime : minTime;
show();
cakes.Enqueue(t);
}
for (int i = 0; i < cn; i++)
{
int t = cakes.Dequeue();
cakeArray[cn - 1] = t;
enumeratQueue(cn - 1);
cakes.Enqueue(t);
}
}
void search(int step)
{
if (step > maxTime)
return;//大于2(n-1)时退出
for (int i = 1; cakeArray [i-1]<cakeArray [i]; i++)
{
if (i == cakeArray.Length - 1)
{
maxTime = step;
return;
}
}//排序成功退出
for (int i = 0; i < cakeArray.Length; i++)
{
revert(0, i);
search(step+1);
revert(0, i);
}//递归穷举所有方案
}
void revert(int begin, int end)
{
int t = cakeArray[begin];
for (int i = begin, j = end; i < j; i++, j--)
{
t = cakeArray[i];
cakeArray[i] = cakeArray[j];
cakeArray[j] = t;
}
}
void show()
{
foreach (int i in cakeArray)
{
Console.Write("{0}\t", i);
}
Console.Write("\nMaxTime is:{0}\tMinTime is:{1}\n",maxTime ,minTime);
}
void setMaxTime()
{
maxTime = 2 * (cakeNum - 1);
}
}
}
最优解(已经使用了书中说的当碰到相同状态时根据是否是是更优解的情况判断,程序还可以继续优化,有兴趣的朋友一起讨论):
运行结果:
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CakeArraySorting
{
class Program
{
Main#region Main
static void Main(string[] args)
{
Program p = new Program();
p.run();
Console.ReadLine();
}
#endregion
Parameters#region Parameters
private int[] cakeSizeArray;//大饼直径队列
private int[] cakeArray;//大饼队列
private int[] maxCakeArray;
private Queue<int> cakes;
const int cakeNum = 9;
private int maxTime = 0;
private int minTime = 0;
private int lowwerTime;
private int ID = 0;
private int[] timeTable;
private DateTime startTime;
private TimeSpan ts;
#endregion
Process#region Process
void run()
{
init();
enumeratQueue(cakes.Count());
ts = DateTime.Now - startTime;
Console.Write("MaxCakeArray:\t");
foreach (int i in maxCakeArray)
{
Console.Write("{0}\t", i);
}
Console.WriteLine("\nCost Time:{0}\t MaxTime:{1}", ts, minTime);
}
void init()
{
//初始化赋值
startTime = DateTime.Now;
setMaxTime();
lowwerTime = (int)15 * cakeNum / 14;
minTime = 0;
cakeArray = new int[cakeNum];
cakeSizeArray = new int[cakeNum];
maxCakeArray = new int[cakeNum];
cakes = new Queue<int>(cakeNum);
setCakes();
cakesEnqueue();
timeTable = new int[getFactorial(cakeNum)];
}
#endregion
AppendMethods#region AppendMethods
void enumeratQueue(int cn)
{
if (cn == 1)
{
int t = cakes.Dequeue();
cakeArray[0] = t;
setMaxTime();
Console.Write("ID:{0}", ID);
//foreach (int i in cakeArray)
//{
// Console.Write("\t{0}", i);
//}
if (getNeberNum() == cakeNum - 1)//无相邻直径大小的饼
{
setTimeTable();
search(0);
minTime = maxTime > minTime ? maxTime : minTime;
if (minTime == maxTime)
Array.Copy(cakeArray, maxCakeArray, cakeNum);
//show();
}
else
{
Console.WriteLine("jump!.");
}
ID++;
cakes.Enqueue(t);
}
for (int i = 0; i < cn; i++)
{
int t = cakes.Dequeue();
cakeArray[cn - 1] = t;
enumeratQueue(cn - 1);
cakes.Enqueue(t);
}
}
void search(int step)
{
//if (cakeNum == 1)
// return;
if (step > maxTime)
return;//大于2(n-1)时退出
if (step + getNeberNum() > maxTime)
return;
int p = getID();
if (step <= timeTable[p])
{
timeTable[p] = step;
}
else
return;
for (int i = 1; cakeArray[i - 1] < cakeArray[i]; i++)
{
if (i == cakeArray.Length - 1)
{
maxTime = step;
return;
}
}//排序成功退出
for (int i = 0; i < cakeArray.Length; i++)
{
revert(0, i);
search(step+1);
revert(0, i);
}//递归穷举所有方案
}
void revert(int begin, int end)
{
int t = cakeArray[begin];
for (int i = begin, j = end; i < j; i++, j--)
{
t = cakeArray[i];
cakeArray[i] = cakeArray[j];
cakeArray[j] = t;
}
}
void show()
{
Console.Write("\nMaxTime is:{0}\tMinTime is:{1}\n",maxTime ,minTime);
}
void setCakes()
{
for (int i = 0; i < cakeSizeArray.Length; i++)
{
cakeSizeArray[i] = i;
}//大饼直径初始化,简便期间直接用相邻数字
}
void cakesEnqueue()
{
foreach (int i in cakeSizeArray)
{
cakes.Enqueue(i);
}//大饼队列化
}
void setMaxTime()
{
//maxTime = 2 * (cakeNum - 1);
maxTime = (5 * cakeNum + 5) / 3;
}
int getNeberNum()
{
int ret = 0;
for (int i = 1; i < cakeNum;i++ )
{
if (Math.Abs(cakeArray[i - 1] - cakeArray[i]) != 1)
{
ret++;
}
}
return ret;
}
int getMaxLowwerNum()//计算15n/14~=n的最大数,计算得为14
{
int i = 1;
for (; 15 * i / 14 - i < 1; i++) ;
return i;
}
int getFactorial(int n)
{
int ret;
for (ret = n; n > 1; n--)
ret *= n - 1;
return ret;
}
int getID( )
{
int ret = 0;
for (int i = 0; i < cakeArray.Length; i++)
{
ret += getNumSize(i) * getFactorial(cakeNum - i - 1);
}
return ret;
}
int getNumSize(int p)
{
int ret=0;
for (int i = p + 1; i < cakeArray.Length; i++)
{
if (cakeArray[p] < cakeArray[i])
ret++;
}
return ret;
}
void setTimeTable()
{
for (int i = 0; i < timeTable.Length; i++)
{
timeTable[i] = (int)(5 * cakeNum + 5) / 3;
}
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CakeArraySorting
{
class Program
{
Main#region Main
static void Main(string[] args)
{
Program p = new Program();
p.run();
Console.ReadLine();
}
#endregion
Parameters#region Parameters
private int[] cakeSizeArray;//大饼直径队列
private int[] cakeArray;//大饼队列
private int[] maxCakeArray;
private Queue<int> cakes;
const int cakeNum = 9;
private int maxTime = 0;
private int minTime = 0;
private int lowwerTime;
private int ID = 0;
private int[] timeTable;
private DateTime startTime;
private TimeSpan ts;
#endregion
Process#region Process
void run()
{
init();
enumeratQueue(cakes.Count());
ts = DateTime.Now - startTime;
Console.Write("MaxCakeArray:\t");
foreach (int i in maxCakeArray)
{
Console.Write("{0}\t", i);
}
Console.WriteLine("\nCost Time:{0}\t MaxTime:{1}", ts, minTime);
}
void init()
{
//初始化赋值
startTime = DateTime.Now;
setMaxTime();
lowwerTime = (int)15 * cakeNum / 14;
minTime = 0;
cakeArray = new int[cakeNum];
cakeSizeArray = new int[cakeNum];
maxCakeArray = new int[cakeNum];
cakes = new Queue<int>(cakeNum);
setCakes();
cakesEnqueue();
timeTable = new int[getFactorial(cakeNum)];
}
#endregion
AppendMethods#region AppendMethods
void enumeratQueue(int cn)
{
if (cn == 1)
{
int t = cakes.Dequeue();
cakeArray[0] = t;
setMaxTime();
Console.Write("ID:{0}", ID);
//foreach (int i in cakeArray)
//{
// Console.Write("\t{0}", i);
//}
if (getNeberNum() == cakeNum - 1)//无相邻直径大小的饼
{
setTimeTable();
search(0);
minTime = maxTime > minTime ? maxTime : minTime;
if (minTime == maxTime)
Array.Copy(cakeArray, maxCakeArray, cakeNum);
//show();
}
else
{
Console.WriteLine("jump!.");
}
ID++;
cakes.Enqueue(t);
}
for (int i = 0; i < cn; i++)
{
int t = cakes.Dequeue();
cakeArray[cn - 1] = t;
enumeratQueue(cn - 1);
cakes.Enqueue(t);
}
}
void search(int step)
{
//if (cakeNum == 1)
// return;
if (step > maxTime)
return;//大于2(n-1)时退出
if (step + getNeberNum() > maxTime)
return;
int p = getID();
if (step <= timeTable[p])
{
timeTable[p] = step;
}
else
return;
for (int i = 1; cakeArray[i - 1] < cakeArray[i]; i++)
{
if (i == cakeArray.Length - 1)
{
maxTime = step;
return;
}
}//排序成功退出
for (int i = 0; i < cakeArray.Length; i++)
{
revert(0, i);
search(step+1);
revert(0, i);
}//递归穷举所有方案
}
void revert(int begin, int end)
{
int t = cakeArray[begin];
for (int i = begin, j = end; i < j; i++, j--)
{
t = cakeArray[i];
cakeArray[i] = cakeArray[j];
cakeArray[j] = t;
}
}
void show()
{
Console.Write("\nMaxTime is:{0}\tMinTime is:{1}\n",maxTime ,minTime);
}
void setCakes()
{
for (int i = 0; i < cakeSizeArray.Length; i++)
{
cakeSizeArray[i] = i;
}//大饼直径初始化,简便期间直接用相邻数字
}
void cakesEnqueue()
{
foreach (int i in cakeSizeArray)
{
cakes.Enqueue(i);
}//大饼队列化
}
void setMaxTime()
{
//maxTime = 2 * (cakeNum - 1);
maxTime = (5 * cakeNum + 5) / 3;
}
int getNeberNum()
{
int ret = 0;
for (int i = 1; i < cakeNum;i++ )
{
if (Math.Abs(cakeArray[i - 1] - cakeArray[i]) != 1)
{
ret++;
}
}
return ret;
}
int getMaxLowwerNum()//计算15n/14~=n的最大数,计算得为14
{
int i = 1;
for (; 15 * i / 14 - i < 1; i++) ;
return i;
}
int getFactorial(int n)
{
int ret;
for (ret = n; n > 1; n--)
ret *= n - 1;
return ret;
}
int getID( )
{
int ret = 0;
for (int i = 0; i < cakeArray.Length; i++)
{
ret += getNumSize(i) * getFactorial(cakeNum - i - 1);
}
return ret;
}
int getNumSize(int p)
{
int ret=0;
for (int i = p + 1; i < cakeArray.Length; i++)
{
if (cakeArray[p] < cakeArray[i])
ret++;
}
return ret;
}
void setTimeTable()
{
for (int i = 0; i < timeTable.Length; i++)
{
timeTable[i] = (int)(5 * cakeNum + 5) / 3;
}
}
#endregion
}
}