算法实验报告.docx
《算法实验报告.docx》由会员分享,可在线阅读,更多相关《算法实验报告.docx(30页珍藏版)》请在冰豆网上搜索。
算法实验报告
中南大学
算法实验
班级信息安全0901
学生王树雄
学号0909090128
教师冯启龙
【实验内容】
1.实现求n皇后问题和子集和数问题的回溯算法。
2.用动态规划的方法实现0/1背包问题。
3.用分支限界法实现0/1背包问题。
4.用深度优化的方法遍历一个图,并判断图中是否有回路存在,如果有,请输出回路。
【实验要求】
所有的输入和输出都用文件处理。
【题目分析】
(1)N皇后问题
N×N皇后问题的求解过程就是一个试探回逆的过程
1、首先查找第一行的可放位置,第一行全部可以放,那么我们就先将第一个皇后放在(0,0)点。
2、再查找第二行,由于第一行的(0,0)已经放了皇后,故第二行的(1,0)和(1,1)都能放皇后了,可放的就是(1,2)和(1,3)点,在(1,2)放上皇后。
3、再查找第三行,查找所以发现第三行没有可放的位置了,回逆到第二行讲皇后放到(1,3)再查找第3行。
如果还不行,就回到第一行将第一行的皇后放人下一个可放的点,依次类推,查找N×N上的所以可放的位置,直到第一行所以位置被放完,输出结果。
4、根据上面的规律可以发现,对于一个皇后放在坐标(x,y),它的下一行位置为(x-1,y)(x,y)(x+1,y)的坐标都不能再放皇后。
我们用一个数组来存放本行能放皇后的点。
用循环来查找上面行对本行的影响,将收到影响的点置FAlSE。
代码:
#include
#include
#include
#defineN4//N皇后
typedefintChessboard[N+1][N+1];//第0号位置不用
intcount=0;
boolcheck(Chessboardcb,inti,intj){//看棋盘cb是否满足合法布局
inth,k;
intm=i+j,n=i-j;
for(h=1;h
if(cb[h][j]==1&&h!
=i)returnfalse;//检查第j列
if(m-h<=N&&cb[h][m-h]==1&&h!
=i)returnfalse;//检查斜的,m-h<=N是为了保证不越界
if(h-n<=N&&cb[h][h-n]==1&&h!
=i)returnfalse;//检查斜的,h-n<=N是为了保证不越界
}
for(k=1;k=j)returnfalse;
returntrue;
}
voidprintfChessboard(Chessboardcb){//打印棋盘
inti,j;
FILE*stream=fopen("a.txt","a");
fprintf(stream,"%s%d%s","(",count,")");
fputs("\n",stream);
for(i=1;i<=N;i++){
for(j=1;j<=N;j++){
printf("%d",cb[i][j]);
fprintf(stream,"%d",cb[i][j]);
}
fputs("\n",stream);
printf("\n");
}
fputs("\n",stream);
fclose(stream);
printf("\n");
}
/*进入本函数时,在n*n棋盘前n-1行已放置了互不攻击的i-1个棋子。
现从第i行起继续为后续棋子选择合适位置。
当i>n时,求得一个合法的布局,输入之。
*/
voidtrial(inti,Chessboard&cb){
intj;
if(i>N){
count++;
printfChessboard(cb);
}
else{
for(j=1;j<=N;j++){
cb[i][j]=1;
if(check(cb,i,j))trial(i+1,cb);
cb[i][j]=0;
}
}
}
intmain(){
inti,j;
Chessboardcb;
for(i=1;i<=N;i++)for(j=1;j<=N;j++)cb[i][j]=0;//必须初始化,它的默认值不是0
trial(1,cb);
system("pause");
return0;
}
(效果图)
当为4个皇后的时候
当为5个皇后的时候
子集和数问题
#include
#include
#defineM31
#defineN4//集合元素个数
intw[N]={13,11,24,7};
intx[N];
voidSubset(ints,intk)//解子集和数问题函数
{
inti,l;l=0;x[l]=k;
while(l>=0)
{
while(s+w[x[l]-1]{
s=s+w[x[l]-1];
k++;l++;
x[l]=k;
}
while(s+w[x[l]-1]>M&&k<=N)
{
k++;x[l]=k;
}
if(s+w[x[l]-1]==M)
{k++;
for(i=0;i<=l;i++)
printf("%d",x[i]);//输出变长解向量
printf("\n");
}
while(k>N)//返回上一个节点,实现回溯的主要思想
{
l--;k=x[l];x[l]=k+1;s=0;
for(i=0;i{
s=s+w[x[i]-1];
}
}
}
}
思想也是用到回溯法。
intmain()
{
printf("数字代表组成结果所在的元素位置\n");
Subset(0,1);//调用subset(ints,intk)函数
system("pause");
return0;
}
运行效果:
(2)动态规划0/1背包
这是最基础的背包问题,特点是:
每种物品仅有一件,可以选择放或不放。
用子问题定义状态:
即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。
则其状态转移方程便是:
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}
这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。
所以有必要将它详细解释一下:
“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。
如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为f[i-1][v];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”,此时能获得的最大价值就是f[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i]。
#include
#include
#defineN50
intmain()
{
intp[N],w[N],m[N][5*N];
inti,j,c,cw,n,sw,sp;
printf("请输入元素个数n:
\n");
scanf("%d",&n);
printf("请输入背包容量c:
\n");
scanf("%d",&c);
for(i=1;i<=n;i++)
{
printf("输入第%d个元素的重量和价值:
\n",i);
scanf("%d",&w[i]);
scanf("%d",&p[i]);
}
for(j=0;j<=c;j++)
if(j>=w[n])
m[n][j]=p[n];
else
m[n][j]=0;
for(i=n-1;i>=1;i--)
for(j=0;j<=c;j++)
if(j>=w[i]&&m[i+1][j]m[i][j]=m[i+1][j-w[i]]+p[i];
else
m[i][j]=m[i+1][j];
cw=c;
printf("\n");
printf("\n");
printf("背包所装物品:
\n");
printf("iW[i]p[i]\n");
for(sp=0,sw=0,i=1;i<=n-1;i++)
if(m[i][cw]>m[i+1][cw])
{
cw-=w[i];sw+=w[i];sp+=p[i];
printf("%2d%3d%3d\n",i,w[i],p[i]);
}
if(m[1][c]-sp==p[n])
{
sw+=w[i];sp+=p[i];
printf("%2d%3d%3d\n",n,w[n],p[n]);
}
printf("w=%d,pmax=%d\n",sw,sp);
printf("\n");
printf("\n");
system("pause");
return0;
}
#include
#include
#defineMAXNUM100
structnode{
intstep;
doubleprice;
doubleweight;
doublemax,min;
unsignedlongpo;
};
typedefstructnodeDataType;
structSeqQueue{/*顺序队列类型定义*/
intf,r;
DataTypeq[MAXNUM];
};
typedefstructSeqQueue*PSeqQueue;
PSeqQueuecreateEmptyQueue_seq(void){
PSeqQueuepaqu;
paqu=(PSeqQueue)malloc(sizeof(structSeqQueue));
if(paqu==NULL)
printf("Outofspace!
!
\n");
else
paqu->f=paqu->r=0;
returnpaqu;
}
intisEmptyQueue_seq(PSeqQueuepaqu){
returnpaqu->f==paqu->r;
}
voidenQueue_seq(PSeqQueuepaqu,DataTypex){
if((paqu->r+1)%MAXNUM==paqu->f)
printf("Fullqueue.\n");
else{
paqu->q[paqu->r]=x;
paqu->r=(paqu->r+1)%MAXNUM;
}
}
分支界限法背包
/*删除队列头元素*/
voiddeQueue_seq(PSeqQueuepaqu){
if(paqu->f==paqu->r)
printf("EmptyQueue.\n");
else
paqu->f=(paqu->f+1)%MAXNUM;
}
/*对非空队列,求队列头部元素*/
DataTypefrontQueue_seq(PSeqQueuepaqu){
return(paqu->q[paqu->f]);
}
/*物品按性价比从新排序*/
voidsort(intn,doublep[],doublew[]){
inti,j;
for(i=0;ifor(j=i;jdoublea=p[j]/w[j];
doubleb=p[j+1]/w[j+1];
if(a
doubletemp=p[j];
p[j]=p[j+1];
p[j+1]=temp;
temp=w[j];
w[j]=w[j+1];
w[j+1]=temp;
}
}
}
/*求最大可能值*/
doubleup(intk,doublem,intn,doublep[],doublew[]){
inti=k;
doubles=0;
while(im-=w[i];
s+=p[i];if(i0){
s+=p[i]*m/w[i];
i++;
}
returns;
}
/*求最小可能值*/
doubledown(intk,doublem,intn,doublep[],doublew[]){
inti=k;
doubles=0;
while(im-=w[i];
s+=p[i];
i++;
}
returns;
}
/*用队列实现分支定界算法*/
doublesolve(doublem,intn,doublep[],doublew[],unsignedlong*po){
doublemin;
PSeqQueueq=createEmptyQueue_seq();
DataTypex={0,0,0,0,0,0};
sort(n,p,w);
x.max=up(0,m,n,p,w);
x.min=min=down(0,m,n,p,w);
if(min==0)return-1;
enQueue_seq(q,x);
while(!
isEmptyQueue_seq(q)){
intstep;
DataTypey;
x=frontQueue_seq(q);
deQueue_seq(q);
if(x.maxstep=x.step+1;
if(step==n+1)continue;
y.max=x.price+up(step,m-x.weight,n,p,w);
if(y.max>=min){
y.min=x.price+down(step,m-x.weight,n,p,w);
y.price=x.price;
y.weight=x.weight;
y.step=step;
y.po=x.po<<1;
if(y.min>=min){
min=y.min;
if(step==n)*po=y.po;
}
enQueue_seq(q,y);
}
if(x.weight+w[step-1]<=m){
y.max=x.price+p[step-1]+
up(step,m-x.weight-w[step-1],n,p,w);
if(y.max>=min){
y.min=x.price+p[step-1]+
down(step,m-x.weight-w[step-1],n,p,w);
y.price=x.price+p[step-1];
y.weight=x.weight+w[step-1];
y.step=step;
y.po=(x.po<<1)+1;
if(y.min>=min){
min=y.min;
if(step==n)*po=y.po;
}
enQueue_seq(q,y);
}
}
}
returnmin;
}
#definen4
doublem=15;
doublep[n]={10,10,12,18};
doublew[n]={2,4,6,9};
intmain(){
inti;
doubled;
unsignedlongpo;
d=solve(m,n,p,w,&po);
if(d==-1)
printf("Nosolution!
\n");
else{
for(i=0;iprintf("x%dis%d\n",i+1,((po&(1<<(n-i-1)))!
=0));
printf("Themaxweightis%f\n",d);
}
getchar();
system("pause");
return0;
}
i++;
}
if(i0){
s+=p[i]*m/w[i];
i++;
}
returns;
}
/*求最小可能值*/
doubledown(intk,doublem,intn,doublep[],doublew[]){
inti=k;
doubles=0;
while(im-=w[i];
s+=p[i];
i++;
}
returns;
}
/*用队列实现分支定界算法*/
doublesolve(doublem,intn,doublep[],doublew[],unsignedlong*po){
doublemin;
PSeqQueueq=createEmptyQueue_seq();
DataTypex={0,0,0,0,0,0};
sort(n,p,w);
x.max=up(0,m,n,p,w);
x.min=min=down(0,m,n,p,w);
if(min==0)return-1;
enQueue_seq(q,x);
while(!
isEmptyQueue_seq(q)){
intstep;
DataTypey;
x=frontQueue_seq(q);
deQueue_seq(q);
if(x.maxstep=x.step+1;
if(step==n+1)continue;
y.max=x.price+up(step,m-x.weight,n,p,w);
if(y.max>=min){
y.min=x.price+down(step,m-x.weight,n,p,w);
y.price=x.price;
y.weight=x.weight;
y.step=step;
y.po=x.po<<1;
if(y.min>=min){
min=y.min;
if(step==n)*po=y.po;
}
enQueue_seq(q,y);
}
if(x.weight+w[step-1]<=m){
y.max=x.price+p[step-1]+
up(step,m-x.weight-w[step-1],n,p,w);
if(y.max>=min){
y.min=x.price+p[step-1]+
down(step,m-x.weight-w[step-1],n,p,w);
y.price=x.price+p[step-1];
y.weight=x.weight+w[step-1];
y.step=step;
y.po=(x.po<<1)+1;
if(y.min>=min){
min=y.min;
if(step==n)*po=y.po;
}
enQueue_seq(q,y);
}}}
returnmin;
}
#definen4
doublem=15;
doublep[n]={10,10,12,18};
doublew[n]={2,4,6,9};
intmain(){
inti;
doubled;
unsignedlongpo;
d=solve(m,n,p,w,&po);
if(d==-1)
printf("Nosolution!
\n");
else{
for(i=0;iprintf("x%dis%d\n",i+1,((po&(1<<(n-i-1)))!
=0));
printf("Themaxweightis%f\n",d);
}
getchar();
system("pause");
return0;
}
实验效果:
1代表该元素被用到,0代表没有用到
(3)深度优先遍历图
从图中某个顶点V0出发,访问此顶点,然后依次从V0的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和V0有路径相通的顶点都被访问到,若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
当以邻接表作存储结构时,深度优先搜索遍历图的时间复杂度为O(n+e)。
#include
#include
#include
#defineMaxVetexNum10
#defineSTACKINITSIZE30
typedefstruct{
int*base;
int*top;
intstacksize;
}SqStack;
typedefstructArcell{
int