for(j=0;j<=i;j++)
{scanf("%d",num[i]+j);mm[i][j]=num[i][j];}
slove();
}
}
4、测试数据
5、算法复杂度分析
时间复杂度O(n)=n+(n-1)+…+1=n*(n-1)/2;即时间复杂度为O(n^2)
空间复杂度为T(n)=3*(n^2);
6、和其他算法比较
很裸的一个动态题目,效率一般,还可以在空间上加以优化,即储存路径的空间可以优化一下,省出一定的空间。
二、二分搜索
1.问题描述
1.1问题概述
LumberjackMirkoneedstochopdownMmetresofwood.Itisaneasyjobforhimsincehehasa
niftynewwoodcuttingmachinethatcantakedownforestslikewildfire.However,Mirkoisonly
allowedtocutasinglerowoftrees.
Mirko?
smachineworksasfollows:
MirkosetsaheightparameterH(inmetres),andthemachine
raisesagiantsawbladetothatheightandcutsoffalltreepartshigherthanH(ofcourse,
treesnothigherthanHmetersremainintact).Mirkothentakesthepartsthatwerecutoff.
Forexample,ifthetreerowcontainstreeswithheightsof20,15,10,and17metres,andMirko
raiseshissawbladeto15metres,theremainingtreeheightsaftercuttingwillbe15,15,10,
and15metres,respectively,whileMirkowilltake5metresoffthefirsttreeand2metresoff
thefourthtree(7metresofwoodintotal).
Mirkoisecologicallyminded,sohedoesn?
twanttocutoffmorewoodthannecessary.That?
swhy
hewantstosethissawbladeashighaspossible.HelpMirkofindthemaximumintegerheightof
thesawbladethatstillallowshimtocutoffatleastMmetresofwood.
1.2输入
Thefirstlineofinputcontainstwospace-separatedpositiveintegers,N(thenumberoftrees,
1≤N≤1000000)andM(Mirko?
srequiredwoodamount,1≤M≤2000000000).
ThesecondlineofinputcontainsNspace-separatedpositiveintegerslessthan1000000000,
theheightsofeachtree(inmetres).ThesumofallheightswillexceedM,thusMirkowillalways
beabletoobtaintherequiredamountofwood.
1.3输出
Thefirstandonlylineofoutputmustcontaintherequiredheightsetting.
1.4事例
SampleInput
47
20151017
520
442402646
SampleOutput
15
36
2.算法分析
对于砍树,一定从在一个唯一的最优解,,所以首先针对于树的高度做一个从小到大的排序,针对于最后的结果M肯定有(i=0,j=n;min(tess[i…j])<=M<=max(tree[i…j]))并且有(getSumTree(i)>getSumTree(M)>getSumTree(j)(i3.核心代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#definein(x)scanf("%d",&x);
#defineout(x)printf("%d\n",x);
usingnamespacestd;
constintN=1000001;
inttrees[N];
intn,m;
inlineboolcmp(constint&a,constint&b)
{returna>b;}
inlinelonglonggetSum(intmid);
intmain()
{
intl,r;
intmid;
longlongsum;
while(scanf("%d%d",&n,&m)!
=EOF)
{
l=r=0;
for(inti=0;i{
in(trees[i]);
r=max(r,trees[i]);
}
sort(trees,trees+n,cmp);
while(l<=r)
{
mid=(l+r)>>1;
sum=getSum(mid);
if(sum<(longlong)m)r=mid-1;
elseif(sum==(longlong)m)break;
elsel=mid+1;
}
out((r+l)/2);
}
}
inlinelonglonggetSum(intmid)
{
longlongsum=0;
for(inti=0;iif(trees[i]>mid)
sum+=trees[i]-mid;
elsebreak;
}
returnsum;
}
4.测试数据
5.算法复杂度分析
时间复杂度O(n*logn)=n+(n-1);
空间复杂度为T(n)=(n);
6.和其他算法比较
标准的二分,时间效率一般,还可以通过其他算法进行时间与优化,但思路简单,容易使用。
三、贪心算法
1.问题描述
1.1问题概述
Oneday,Tpkeyhasnothingtodowhenheglancesatsomekidsplayinggamesongridarea.Saddenlyhegetsanidea,
inventinganewmazegame.Firsthedrawsabiggridareaconsistingofn*nsmallgrid.Thegridareahasacompeletlycenter.
Theplayerwillstandatthecenter,thenheorshewasgivenahandofdirectionalorder,indicadingthattheplayer
shouldjumptotheneighbouringgridsuccessivelyinthisordereddirection.Butsomeofthisorderisreplacedby"?
",
sothattheplayercanjumpatanydirection.Thewinerwillbetheonewhoistheclosesttothegridareaboundary
intheprocessofjumping.Thenakidisplayingthegame,buthestop,thenhegiveathoughttoguaranteehewill
bethewiner.Hefeelsoconfused,butheknowyouareagoodcoder.Heneedsyourhelp
1.2输入
Thefirstofinputisanintegert(0foreachcasewillincludetwolnes,anumber(int)andastring(length<1000),
thenumber'smeansisthatTpkeywilldrawasquareareawhichhasn*nsmallsquare.
andthestringwillincludefiveletters"EWSN?
"whichstandsfor"EASTWESTSOUTHNORTHandUndetermined"
andthestringlengthatmost1000.
1.3输出
foreachcase,theoutputshouldcontainasinglerealnumber,onasingleline:
theminimumdistancefromthesquareborderwhenhejumpedbytheinstructions
1.4样例
15
?
?
?
?
?
2
15
EE?
WWW
4
29
WS?
W?
S?
W?
SES
6
2.算法分析
很裸的贪心问题,遇到?
号的时候,对应的四个方向都可以选择,所以在前一次的基础之上都进行加1;最终结果所有的“问号”肯定为四种方向中的一种方向(对于最优宁愿同向也不怨反向或者不影响),所以可得,边算边保存中间最大,则就为题目离中心最远的距离,即为最优解。
3.核心代码
#include
#include
#include
#include
#include
#definein(x)scanf("%d",&x);
#defineout(x)printf("%d\n",x);
usingnamespacestd;
constintN=1005;
charstr[N];
intmain()
{
intnn;
intt;
scanf("%d",&t);
for(inttt=0;tt{
scanf("%d\n",&nn);
scanf("%s",str);
intsum=0;
inte(0);
ints(0);
intw(0);
intn(0);
nn/=2;
for(inti=0;i{
switch(str[i])
{
case'W':
w++;e--;
break;
case'E':
e++;w--;
break;
case'N':
n++;s--;
break;
case'S':
s++;n--;
break;
case'?
':
s++;n++;w++;e++;
break;
}
sum=max(sum,max(max(e,w),max(n,s)));
if(sum>nn)
{sum=nn;break;}
}
printf("Case#%d:
%d\n",tt+1,nn-sum);
}
}
4.测试数据
5.算法复杂度分析
时间复杂度o(n)的算法无法优化。
空间复杂度为T(n);可以进行优化达到T
(1)的算法。
使用getchar来进行输入。
6.和其他算法比较
很裸的一个贪心,算是最高效的算法,在空间上可以优化一些,做到无长数组的变量。
四、回溯法
1.问题描述
在一个N*N的棋盘上放置N个皇后,且使得每两个之间不能互相攻击,也就是使得每两个不在同一行,同一列和同一斜角线上。
2.算法分析
对于N=1,问题的解很简单,而且我们很容易看出对于N=2和N=3来说,这个问题是无解的。
所让我们考虑4皇后问题并用回溯法对它求解。
因为每个皇后都必须分别占据—行,我们需要做的不过是为棋盘上的每个皇后分配一列。
我们从空棋盘开始,然后把皇后1放到它所在行的第一个可能位置上,也就是第一行第—列。
对于皇后2,在经过第一列和第二列的失败尝试之后,我们把它放在第一个可能的位置,就是格子〔2,3),位于第二行第二列的格子。
这被证明是一个死胡同,因为皇后:
将没有位置可放。
所以,该算法进行回溯,把皇后2放在下一个可能位置(2,4)上。
然后皇后3就可以放在(3,2),这被证明是另一个死胡同。
该算法然后就回溯到底,把皇后1移到(1,2)。
接着皇后2到(2,4),皇后3到(3,1),而皇后4到(4,3),这就是该问题的一个解。
通过以此类推的关系得出相应输入N的结果。
3.核心代码
#include
#include
#include
#include
usingnamespacestd;
#defineN4
intcol[N+1];
//输出结果
voidOutput()
{
for(inti=1;i<=N;i++)
{
printf("(%d,%d)\n",i,col[i]);
}
printf("\n");
}
//求解函数
voidQueen(inti,intn)
{
if(i>n)
Output();
else
{
for(intj=1;j<=n;++j)
{
intk=1;
col[i]=j;
while(k
{
if((col[k]-col[i])*(fabs(col[k]-col[i])-fabs(k-i))!
=0)
{
k++;
if(k==i)
Queen(i+1,n);
}
else
{
break;
}
}
}
}
}
intmain()
{
printf("theansweris:
\n");
for(inti=1;i<=N;i++)
{
col[1]=i;//设置第一行
Queen(2,N);
}
system("pause");
}
4.测试数据
N==4N==5
5.算法复杂度分析
在N较小的时候时间效率很高,N在较大的时候,对于棋盘的扫描很多,使用数组的存储与加法的运算时的空间与时间上的效率都是很低的。
6.和其他算法比较
通过使用回溯的算法在一定程度上避免了一下多余操作,但算法并不是最优的,在处理较大的N时候可以进行优化。
优化1:
程序中的所有数在计算机内存中都是以二进制的形式储存的,而位运算就是直接对整数在内存中的二进制位进行操作,所以速度快,效率高。
因此选择用位运算来改进运算速度;优化2:
改进思路,对于不同的皇后问题,使用不同的方法计算,如,对于除2、3、8、9、14、15、26、27、38、39之外的任意N值皇后,可以用分治法进行计算。
(但不适合对于结果的输出)
五、分支限界法
1、问题描述
在3*3的棋盘上有8个将牌,每一个将牌都刻有1-8数码中的某一个数码。
棋盘中留有一个空格,允许其周围的某一个将牌向空格移动,这样通过移动将牌就可以不断改变将牌的布局。
给定一种初始时的将牌布局或结构(称作初始状态)和一个目标的布局(称作目标状态),问如何移动将牌,实现从初始状态到目标状态的改变,给出合法步骤中最小的移动步数。
如下图所示
输入格式:
首先输入一个数字t(t>=1&&t<=10)为测试数据的总数,接下来是t组测试数据,每组测试数据均由6行,每行3个数字组成,前3行为初始状态,后3行为目标状态,空格由0表示,每组测试数据前面有一个空行。
输出格式:
给出可以使初始状态转换到目标状态的最小移动次数,如果转化不到打印出-1。
输入样例:
2
283
164
705
230
184
765
283
164
705
283
146
705
输出样例:
3
-1
2、算法分析
很裸的一个A*搜索,中间加启发函数进行限定使得效率有大大的提高.首先声明一个一个结构体node来储存程序处理中间的状态八宫格的状态以及处理到这一步的值等。
其中num表示的将八宫格状态转换成一个整数,a[]怎保存的是起每个格子的值,zero表示空格的位置,ti表示已经走的步数,per估计值的结果,表示到目标点逆序数之和,h表示已走步数与估计值的和,代表这启发函数,并用来作为排序限定的作用,避免无效的搜索。
本题主要用的基础算法是BFS作为搜索,将每一次的扩展判定有效性,将有效的结果加入到优先队列中去,优先队列通过其启发函数进行排序,将较优的状态放到队头,以便及早扫描,,并用两个map进行标记,表示一个已经扫描过,且无效的,另一个表示正在扫描中,作为一些扫描值的限定,当扫描值等于要求结果值时,则寻找成功。
3、核心代码
/*A*搜索....*/
#include
#include
#include
#include
#include
#include
#include
usingnamespacestd;
intten[10]={1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
intzt[][4]={{-1,-1,3,1},{-1,0,4,2},{-1,1,5,-1},{0,-1,6,4},{1,3,7,5},{2,4,8,-1},{3,-1,-1,7},{4,6,-1,8},{5,7,-1,-1}};
structnode;
structnode{
intnum;
inta[9];
intzero;
intti;
intpre;
inth;
voidsetPer(constnode&end)
{
pre=0;
for(inti=0;i<9;i++)
{if(a[i]==0)continue;
for(intj=0;j<9;j++)
{if(end.a[j]==a[i])
pre+=abs(i%3-j%3)+abs(i/3-j/3);
}
}
h=pre+ti;
}
voidsetNum()
{
num=0;
for(intj=0;j<9;j++)
{num+=ten[8-j]*a[j];
if(a[j]=