ACM程序设计竞赛例题.docx
《ACM程序设计竞赛例题.docx》由会员分享,可在线阅读,更多相关《ACM程序设计竞赛例题.docx(52页珍藏版)》请在冰豆网上搜索。
ACM程序设计竞赛例题
备战ACM资料
习题
1.0-1背包问题
在0/1背包问题中,需对容量为c的背包进行装载。
从n个物品中选取装入背包的物品,每件物品i的重量为wi,价值为pi。
对于可行的背包装载,背包中物品的总重量不能超过背包的容量,最佳装载是指所装入的物品价值最高。
程序如下:
#include
voidreaddata();
voidsearch(int);
voidcheckmax();
voidprintresult();
intc=35,n=10;//c:
背包容量;n:
物品数
intw[10],v[10];//w[i]、v[i]:
第i件物品的重量和价值
inta[10],max;//a数组存放当前解各物品选取情况;max:
记录最大价值
//a[i]=0表示不选第i件物品,a[i]=1表示选第i件物品
intmain()
{
readdata();//读入数据
search(0);//递归搜索
printresult();
}
voidsearch(intm)
{
if(m>=n)
checkmax();//检查当前解是否是可行解,若是则把它的价值与max比较
else
{
a[m]=0;//不选第m件物品
search(m+1);//递归搜索下一件物品
a[m]=1;//不选第m件物品
search(m+1);//递归搜索下一件物品
}
}
voidcheckmax()
{
inti,weight=0,value=0;
for(i=0;i{
if(a[i]==1)//如果选取了该物品
{
weight=weight+w[i];//累加重量
value=value+v[i];//累加价值
}
}
if(weight<=c)//若为可行解
if(value>max)//且价值大于max
max=value;//替换max
}
voidreaddata()
{
inti;
for(i=0;iscanf("%d%d",&w[i],&v[i]);//读入第i件物品重量和价值
}
voidprintresult()
{
printf("%d",max);
}
2.装载问题
有两艘船,载重量分别是c1、c2,n个集装箱,重量是wi(i=1…n),且所有集装箱的总重量不超过c1+c2。
确定是否有可能将所有集装箱全部装入两艘船。
提示:
求出不超过c1的最大值max,若总重量-max3.堡垒问题(ZOJ1002)
如图城堡是一个4×4的方格,为了保卫城堡,现需要在某些格子里修建一些堡垒。
城堡中的某些格子是墙,其余格子都是空格,堡垒只能建在空格里,每个堡垒都可以向上下左右四个方向射击,如果两个堡垒在同一行或同一列,且中间没有墙相隔,则两个堡垒都会把对方打掉。
问对于给定的一种状态,最多能够修建几个堡垒。
程序主要部分如下:
intmain()
{
readdata();//读入数据
search(0);//递归搜索
printresult();
}
voidsearch(intm)
{
introw,col;
row=m/n;//求第m个格子的行号
col=m%n;//求第m个格子的列号
if(m>=n*n)
checkmax();//检查当前解是否是可行解,若是则把它的价值与max比较
else
{
search(m+1);//该位置不放堡垒递归搜索下一个位置
if(canplace(m))//判断第m个格子是否能放堡垒
{
place(m);//在第m个格子上放置一个堡垒
search(m+1);//递归搜索下一个位置
takeout(m);//去掉第m个格子上放置的堡垒
}
}
}
5.8皇后问题
在一个8×8的棋盘里放置8个皇后,要求这8个皇后两两之间互相都不“冲突”。
#include
#include
voidsearch(int);
voidprintresult();//打印结果
intcanplace(int,int);//判断该位置能否放置皇后
voidplace(int,int);//在该位置能否放置皇后
voidtakeout(int,int);//把该位置放置皇后去掉
inta[8];//a[i]存放第i个皇后的位置
intmain()
{
search(0);//递归搜索
}
voidsearch(intm)
{
inti;
if(m>=8)//当已经找出一组解时
printresult();//输出当前结果
else
{
for(i=0;i<8;i++)//对当前行0到7列的每一个位置
{
if(canplace(m,i))//判断第m个格子是否能放堡垒
{
place(m,i);//在(m,i)格子上放置一个皇后
search(m+1);//递归搜索下一行
takeout(m,i);//把(m,i)格子上的皇后去掉
}
}
}
}
intcanplace(introw,intcol)
{
inti;
for(i=0;iif(abs(i-row)==abs(a[i]-col)||a[i]==col)
return(0);
return
(1);
}
voidplace(introw,intcol)
{
a[row]=col;
}
voidtakeout(introw,intcol)
{
a[row]=-1;
}
voidprintresult()
{
inti,j;
for(i=0;i<8;i++)
{
for(j=0;j<8;j++)
if(a[i]==j)
printf("A");
else
printf(".");
printf("\n");
}
printf("\n");
}
6.素数环问题
把从1到20这20个数摆成一个环,要求相邻的两个数的和是一个素数。
分析:
用回溯算法,考察所有可能的排列。
程序如下:
#include
#include
voidsearch(int);
voidinit();//初始化
voidprintresult();//打印结果
intisprime(int);//判断该数是否是素数
voidswap(int,int);//交换a[m]和a[i]
inta[21];//a数组存放素数环
intmain()
{
init();
search
(2);//递归搜索
}
intisprime(intnum)
{
inti,k;
k=sqrt(num);
for(i=2;i<=k;i++)
if(num%i==0)
return(0);
return
(1);
}
voidprintresult()
{
inti;
for(i=1;i<=20;i++)
printf("%3d",a[i]);
printf("\n");
}
voidsearch(intm)
{
inti;
if(m>20)//当已经搜索到叶结点时
{
if(isprime(a[1]+a[20]))//如果a[1]+a[20]也是素数
printresult();//输出当前解
return;
}
else
{
for(i=m;i<=20;i++)//(排列树)
{
swap(m,i);//交换a[m]和a[i]
if(isprime(a[m-1]+a[m]))//判断a[m-1]+a[m]是否是素数
search(m+1);//递归搜索下一个位置
swap(m,i);//把a[m]和a[i]换回来
}
}
}
voidswap(intm,inti)
{
intt;
t=a[m];
a[m]=a[i];
a[i]=t;
}
voidinit()
{
inti;
for(i=0;i<21;i++)
a[i]=i;
}
7.迷宫问题
给一个20×20的迷宫、起点坐标和终点坐标,问从起点是否能到达终点。
输入数据:
’.’表示空格;’X’表示墙。
程序如下:
#include
#include
voidsearch(int,int);
intcanplace(int,int);
voidreaddata();//读入数据
voidprintresult();//打印结果
inta[20][20];//a数组存放迷宫
ints,t;
intmain()
{
introw,col;
readdata();
row=s/20;
col=s%20;
search(row,col);//递归搜索
printresult();
}
voidsearch(introw,intcol)
{
intr,c;
a[row][col]=1;
r=row;//左
c=col-1;
if(canplace(r,c))//判断(r,c)位置是否已经走过
search(r,c);//递归搜索(r,c)
r=row+1;//下
c=col;
if(canplace(r,c))//判断(r,c)位置是否已经走过
search(r,c);//递归搜索(r,c)
r=row;//右
c=col+1;
if(canplace(r,c))//判断(r,c)位置是否已经走过
search(r,c);//递归搜索(r,c)
r=row-1;//上
c=col;
if(canplace(r,c))//判断(r,c)位置是否已经走过
search(r,c);//递归搜索(r,c)
}
voidprintresult()
{
inti,j;
for(i=0;i<20;i++)
{
for(j=0;j<20;j++)
printf("%3d",a[i][j]);
printf("\n");
}
}
voidreaddata()
{
inti,j;
for(i=0;i<20;i++)
{
for(j=0;j<20;j++)
scanf("%d",&a[i][j]);
}
}
intcanplace(introw,intcol)
{
if(row>=0&&row<20&&col>=0&&col<20&&a[row][col]==0)
return1;
else
return0;
}
}
1.Floodfill
给一个20×20的迷宫和一个起点坐标,用广度优先搜索填充所有的可到达的格子。
提示:
参考第2题。
2.电子老鼠闯迷宫
如下图12×12方格图,找出一条自入口(2,9)到出口(11,8)的最短路
本题给出完整的程序和一组测试数据。
状态:
老鼠所在的行、列。
程序如下:
#include
voidreaddata();//读入数据
voidinit();//初始化
intsearch();//广搜,并在每一个可到达的每一个空格出填上最小步数
intemptyopen();//判栈是否为空:
空:
1;非空:
0。
inttakeoutofopen();//从栈中取出一个元素,并把该元素从栈中删除
intcanmoveto(int,int,int*,int*,int);//判能否移动到该方向,并带回坐标(r,c)
intisaim(introw,intcol);//判断该点是否是目标
intused(int,int);//判断该点是否已经走过
voidaddtoopen(int,int);//把该点加入到open表
inta[12][12];//a存放迷宫,0表示空格,-2表示墙。
//广搜时,未找到目标以前到达的空格,填上到达该点的最小步数
intn;//n为迷宫边长,注:
若大于12,必须修改一些参数,如a的大小
intopen[20],head,tail,openlen=20;//open表
ints,t;//起点和终点
intmain()
{
intnumber;
readdata();//读取数据
init();//初始化
number=search();//广搜并返回最小步数
printf("%d",number);//打印结果
}
intsearch()
{
intu,row,col,r,c,i,num;
while(!
emptyopen())//当栈非空
{
u=takeoutofopen();//从栈中取出一个元素,并把该元素从栈中删除
row=u/n;//计算该点的坐标
col=u%n;
num=a[row][col];//取得该点的步数
for(i=0;i<4;i++)
{
if(canmoveto(row,col,&r,&c,i))//判能否移动到该方向,并带回坐标(r,c)
{
if(isaim(r,c))//如果是目标结点
return(num+1);//返回最小步数
if(!
used(r,c))//如果(r,c)还未到达过
{
a[r][c]=num+1;//记录该点的最小步数
addtoopen(r,c);//把该点加入到open表
}
}
}
}
}
intemptyopen()
{
if(head==tail)
return
(1);
else
return(0);
}
inttakeoutofopen()
{
intu;
if(head==tail)
{
printf("errer:
stackisempty");
return(-1);
}
u=open[head++];
head=head%openlen;
return(u);
}
intcanmoveto(introw,intcol,int*p,int*q,intdirection)
{
intr,c;
r=row;
c=col;
switch(direction)
{
case0:
c--;//左
break;
case1:
r++;//下
break;
case2:
c++;//右
break;
case3:
r--;//上
}
*p=r;
*q=c;
if(r<0||r>=n||c<0||c>=n)//如果越界返回0
return(0);
if(a[r][c]==0)//如果是空格返回1
return
(1);
return(0);//其余情况返回0
}
intisaim(introw,intcol)
{
if(row*n+col==t)
return
(1);
else
return(0);
}
intused(introw,intcol)
{
if(a[row][col]==0)//0表示空格
return(0);
else
return
(1);
}
voidaddtoopen(introw,intcol)
{
intu;
u=row*n+col;
open[tail++]=u;
tail=tail%openlen;
}
voidreaddata()
{
inti,j,row,col;
charstr[20];
scanf("%d",&n);
scanf("%d%d",&row,&col);//起点坐标
s=row*n+col;
scanf("%d%d",&row,&col);//终点坐标
t=row*n+col;
gets(str);
for(i=0;i{
gets(str);
for(j=0;jif(str[j]=='.')
a[i][j]=0;//0表示空格
else
a[i][j]=-2;//-2表示墙
}
}
voidinit()
{
head=0;
tail=1;
open[0]=s;
}
测试数据如下:
1210718
XXXXXXXXXXXX
X......X.XXX
X.X.XX.....X
X.X.XX.XXX.X
X.X.....X..X
X.XXXXXXXXXX
X...X.X....X
X.XXX...XXXX
X.....X....X
XXX.XXXX.X.X
XXXXXXX..XXX
XXXXXXXXXXXX
注:
测试数据可在运行时粘贴上去(点击窗口最左上角按钮,在菜单中选则“编辑”/“粘贴”即可)。
想一想:
此程序都存在哪些问题,如果openlen太小程序会不会出错,加入代码使程序能自动报出此类错误。
3.跳马
给一个200×200的棋盘,问国际象棋的马从给定的起点到给定的终点最少需要几步。
SampleInput0011Sampleoutput4
状态:
马所在的行、列。
程序如下:
#include
voidreaddata();//读入数据
voidinit();//初始化
intsearch();//广度优先搜索
intemptyopen();//判栈是否为空:
空:
1;非空:
0。
longtakeoutofopen();//从栈中取出一个元素,并把该元素从栈中删除
intcanmoveto(int,int,int*,int*,int);//判能否移动到该方向,并带回坐标(r,c)
intisaim(introw,intcol);//判断该点是否是目标
intused(int,int);//判断该点是否已经走过
voidaddtoopen(int,int);//把该点加入到open表
inta[200][200],n=200;//a存放棋盘,n为迷宫边长
longopen[2000],head,tail,openlen=2000;//open表1367
longs,t;//起点和终点
intsearch()
{
longu;
introw,col,r,c,i,num;
while(!
emptyopen())//当栈非空
{
u=takeoutofopen();//从栈中取出一个元素,并把该元素从栈中删除
row=u/n;//计算该点所在的行
col=u%n;//计算该点所在的列
num=a[row][col];//取得该点的步数
for(i=0;i<8;i++)
{
if(canmoveto(row,col,&r,&c,i))//判能否移动到该方向,并带回坐标(r,c)
{
if(isaim(r,c))//如果是目标结点
return(num+1);//返回最小步数
if(!
used(r,c))//如果(r,c)还未到达过
{
a[r][c]=num+1;//记录该点的最小步数
addtoopen(r,c);//把该点加入到open表
}
}
}
}
return-1;
}
intmain()//为了让search()显示在一页内和main函数换了以下
{//一般的算法程序main函数写在最上面读起来更方便
intnumber;
readdata();//读取数据
init();//初始化
number=search();//广搜并返回最小步数
printf("%d",number);//打印结果
}
intemptyopen()
{
if(head==tail)
return
(1);
else
return(0);
}
longtakeoutofopen()
{
longu;
if(head==tail)
{
printf("errer:
stackisempty");
return(-1);
}
u=open[head++];
head=head%openlen;
return(u);
}
intused(introw,intcol)
{
if(a[row][col]==0)
return(0);
else
return
(1);
}
voidaddtoopen(introw,intcol)
{
intu;
if((head-tail)%openlen==1)
printf("opentableoverflow");
u=row;
u=u*n+col;
open[tail++]=u;
tail=tail%openlen;
}
voidreaddata()
{
longrow,col;
scanf("%ld%ld",&row,&col);//起点坐标
s=row*n+col;
scanf("%ld%ld",&row,&col);//终点坐标
t=row*n+col;
}
voidinit()
{
head=0;
tail=1;
open[0]=s;
}
intisaim(introw,intcol)
{
if(row*n+col==t)
return
(1);
else
return(0);
}
思考:
参考第4题,改为用结构体表示状态写此程序。
4.独轮车
独轮车的轮子上有5种颜色,每走一格颜色变化一次,独轮车只能往前推,也可以在原地旋转,每走一格,需要一个单位的时间
|