acm备考资料整理by liangge.docx

上传人:b****7 文档编号:10853194 上传时间:2023-02-23 格式:DOCX 页数:118 大小:263.03KB
下载 相关 举报
acm备考资料整理by liangge.docx_第1页
第1页 / 共118页
acm备考资料整理by liangge.docx_第2页
第2页 / 共118页
acm备考资料整理by liangge.docx_第3页
第3页 / 共118页
acm备考资料整理by liangge.docx_第4页
第4页 / 共118页
acm备考资料整理by liangge.docx_第5页
第5页 / 共118页
点击查看更多>>
下载资源
资源描述

acm备考资料整理by liangge.docx

《acm备考资料整理by liangge.docx》由会员分享,可在线阅读,更多相关《acm备考资料整理by liangge.docx(118页珍藏版)》请在冰豆网上搜索。

acm备考资料整理by liangge.docx

acm备考资料整理byliangge

背包问题

P01:

01背包问题

题目

有N件物品和一个容量为V的背包。

第i件物品的费用是c[i],价值是w[i]。

求解将哪些物品装入背包可使价值总和最大。

基本思路

这是最基础的背包问题,特点是:

每种物品仅有一件,可以选择放或不放。

用子问题定义状态:

即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。

则其状态转移方程便是:

f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}

先考虑上面讲的基本思路如何实现,肯定是有一个主循环i=1..N,每次算出来二维数组f[i][0..V]的所有值。

那么,如果只用一个数组f[0..V],能不能保证第i次循环结束后f[v]中表示的就是我们定义的状态f[i][v]呢?

f[i][v]是由f[i-1][v]和f[i-1][v-c[i]]两个子问题递推而来,能否保证在推f[i][v]时(也即在第i次主循环中推f[v]时)能够得到f[i-1][v]和f[i-1][v-c[i]]的值呢?

事实上,这要求在每次主循环中我们以v=V..0的顺序推f[v],这样才能保证推f[v]时f[v-c[i]]保存的是状态f[i-1][v-c[i]]的值。

伪代码如下:

fori=1..N

forv=V..0

f[v]=max{f[v],f[v-c[i]]+w[i]};

其中的f[v]=max{f[v],f[v-c[i]]}一句恰就相当于我们的转移方程f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]},因为现在的f[v-c[i]]就相当于原来的f[i-1][v-c[i]]。

如果将v的循环顺序从上面的逆序改成顺序的话,那么则成了f[i][v]由f[i][v-c[i]]推知,与本题意不符,但它却是另一个重要的背包问题P02最简捷的解决方案,故学习只用一维数组解01背包问题是十分必要的。

事实上,使用一维数组解01背包的程序在后面会被多次用到,所以这里抽象出一个处理一件01背包中的物品过程,以后的代码中直接调用不加说明。

过程ZeroOnePack,表示处理一件01背包中的物品,两个参数cost、weight分别表明这件物品的费用和价值。

procedureZeroOnePack(cost,weight)

forv=V..cost

f[v]=max{f[v],f[v-cost]+weight}

初始化的细节问题

我们看到的求最优解的背包问题题目中,事实上有两种不太相同的问法。

有的题目要求“恰好装满背包”时的最优解,有的题目则并没有要求必须把背包装满。

一种区别这两种问法的实现方法是在初始化的时候有所不同。

如果是第一种问法,要求恰好装满背包,那么在初始化时除了f[0]为0其它f[1..V]均设为-∞,这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解。

如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将f[0..V]全部设为0。

为什么呢?

可以这样理解:

初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。

如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。

如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。

这个小技巧完全可以推广到其它类型的背包问题,后面也就不再对进行状态转移之前的初始化进行讲解。

一个常数优化

前面的伪代码中有forv=V..1,可以将这个循环的下限进行改进。

由于只需要最后f[v]的值,倒推前一个物品,其实只要知道f[v-w[n]]即可。

以此类推,对以第j个背包,其实只需要知道到f[v-sum{w[j..n]}]即可,即代码中的

fori=1..N

forv=V..0

可以改成

fori=1..n

bound=max{V-sum{w[i..n]},c[i]}

forv=V..bound

这对于V比较大时是有用的。

输入格式

输入数据首先包含一个正整数C,表示有C组测试用例,每组测试用例的第一行是两个整数n和m(1<=n<=100,1<=m<=100),分别表示经费的金额和大米的种类,然后是m行数据,每行包含3个数p,h和c(1<=p<=20,1<=h<=200,1<=c<=20),分别表示每袋的价格、每袋的重量以及对应种类大米的袋数。

输出格式

对于每组测试数据,请输出能够购买大米的最多重量,你可以假设经费买不光所有的大米,并且经费你可以不用完。

每个实例的输出占一行。

输入样例

1

82

21004

41002

输出样例

400

#include

#include

usingnamespacestd;

#defineN1005

#defineMax(a,b)((a)>(b)?

(a):

(b))

#defineMin(a,b)((a)<(b)?

(a):

(b))

intdp[N][N];

intmain()

{

intt;

inti,j,k,am,a;

intn,m;

intmax;

intvol[N],val[N],nn[N];

cin>>t;

while(t--)

{

cin>>m>>n;

for(i=1;i<=n;i++)

cin>>vol[i]>>val[i]>>nn[i];

memset(dp,0,sizeof(dp));

for(i=1;i<=n;i++)

for(j=m;j>=0;j--)

{

max=-1;

for(k=0;k<=nn[i]&&k*vol[i]<=j;k++)

max=Max(max,dp[i-1][j-k*vol[i]]+k*val[i]);

dp[i][j]=max;

}

cout<

}

return0;

}

下面为0-1背包问题,上面为多重背包

#include

usingnamespacestd;

#defineMAXSIZE1000

intf[MAXSIZE+1],c[MAXSIZE+1],w[MAXSIZE+1];

intmain()

{

intN,V;

cin>>V>>N;

inti=1;

for(;i<=N;++i)

{

cin>>c[i]>>w[i];

}

for(i=1;i<=N;++i)

{

for(intv=V;v>=c[i];--v)//c[i]可优化为bound,bound=max{V-sumc[i,...n],c[i]}

{

f[v]=(f[v]>f[v-c[i]]+w[i]?

f[v]:

f[v-c[i]]+w[i]);

}

}

//当i=N时,可以跳出循环单独计算F[V]

cout<

return0;

}

背包问题,贪心法

#include

#include

#include

usingnamespacestd;

inti,j,n;

floatW,x[1000],sum;

structrm

{

floatw;

floatv;

}room[1000];

boolcmp(rma,rmb)

{

returna.v/a.w>b.v/b.w;

}

intmain()

{

cin>>W>>n;

while(n!

=-1&&W!

=-1)

{

for(i=0;i

cin>>room[i].v>>room[i].w;

sort(room,room+n,cmp);

memset(x,0,1000);

i=0;

while(room[i].w

{

x[i]=room[i].v;

W=W-room[i].w;

i++;

}

x[i]=room[i].v/room[i].w*W;

j=0;sum=0.0;

while(j<=i)

{

sum+=x[j];

j++;

}

cout<

:

fixed)<

cin>>W>>n;

}

return0;

}

输入样例

53

72

43

52

-1-1(结束)

输出样例

13.333对v/w从大到小排列,再依次选择

八皇后问题

#include

#include

#include

usingnamespacestd;

structnode1

{boolb[8][8];};

structnode2

{intx,y;};

node1visited[9];

node2zb[8];

intnum;

voidprint()

{printf("case%d:

",++num);

for(inti=0;i<=7;i++)

{printf("%d,%d\t",zb[i].x,zb[i].y);}

cout<<'\n';

}

intx1,y1,x4,y4;

voidvis(intx,inty,intstep){

x1=x;y1=y;

x4=x;y4=y;

visited[step]=visited[step-1];

for(inti=0;i<8;i++)

{

visited[step].b[x][i]=0;

visited[step].b[i][y]=0;

}

while(x1<8&&y1<8)

{

visited[step].b[x1][y1]=0;

x1++;y1++;

}

while(x4<8&&y4>=0)

{

visited[step].b[x4][y4]=0;

x4++;y4--;

}

}

voidDFS(intstep)

{

if(step==9)

print();

else

{

for(intj=0;j<8;j++)

if(visited[step-1].b[step-1][j])

{

zb[step-1].x=step-1;

zb[step-1].y=j;

vis(step-1,j,step);

DFS(step+1);

}}}

intmain(){

num=0;

memset(visited,1,sizeof(visited));

DFS

(1);

//cout<<"helloworld"<

system("pause");

}

站位问题:

#include

#include

usingnamespacestd;

intcounter=0;

voiddisplay(intplace[])

{

for(inti=1;i<=6;i++)

{

cout<

if(i%3==0)

cout<<"\n";

}

cout<

}

booljudge(intmark,intplace[])

{

switch(mark)

{

case1:

returntrue;

case2:

returnplace[2]!

=5&&place[2]!

=6;

case3:

returnplace[3]!

=2&&place[3]!

=3;

case4:

returnplace[4]!

=1&&place[4]!

=2&&place[4]!

=3&&place[4]!

=6;

case5:

returnplace[5]!

=1&&place[5]!

=5&&place[5]!

=6;

case6:

returnplace[6]!

=1&&place[6]!

=6;

}

}

boolothers(intplace[])

{

intm=1,n=1;

for(inti=0;i<=6;i++)

{

if(place[i]==3)

m=i-1;

if(place[i]==4)

n=i-1;

}

returnm/3!

=n/3;

}

voidbacktrace(intmark,intplace[],intpeople[])

{

for(inti=1;i<=6;i++)

{

if(people[i]!

=0)

{

place[mark]=people[i];

if(judge(mark,place))

{

if(mark==6&&others(place))

{

counter++;

display(place);

}

else

{

people[i]=0;

backtrace(mark+1,place,people);

people[i]=i;

}

}

}

}

}

intmain(intargc,char*argv[])

{

int*place=newint[7];

int*people=newint[7];

for(inti=1;i<=6;i++)

people[i]=i;

backtrace(1,place,people);

cout<<"Thetotalmethodsis"<

system("PAUSE");

returnEXIT_SUCCESS;

}

把一个数分解为若干数的和

#include

#include

#defineMAXSIZE50

staticintbuff[MAXSIZE];

intSum(intbuff[],inti)

{

intsum=0;

for(intm=0;m<=i;m++)

sum+=buff[m];

returnsum;

}

voidprintFactor(intbuff[],inti,constintnumber)

{

intcount=0,m;

for(m=0;m<=i;m++)

{

printf("%4d",buff[m]);

}

if(number==buff[0])

printf("%4d",0);

printf("\n");

}

voiddivide(inti,inttop,intnumber)

{

intj;

for(j=i;j>0;j--)

{

buff[top]=j;

if(number==Sum(buff,top))

printFactor(buff,top,number);

elseif(number

continue;

if(top>=number)

continue;

divide(j,top+1,number);

}

return;

}

voidmain()

{

inti,top=0;

intnumber;

printf("inputasoucenumber:

");

scanf("%d",&number);

i=number;

divide(i,top,number);

}

普里姆prim最小生成数

#include

#include

#defineN100

intp[N],key[N],tb[N][N];//key[j]表示到达j点的路径

voidprim(intv,intn){

inti,j;

intmin;

for(i=1;i<=n;i++){//从第一个顶点开始找

p[i]=v;

key[i]=tb[v][i];//初始化到达i点的各个值

}

key[v]=0;

for(i=2;i<=n;i++){

min=INT_MAX;

for(j=1;j<=n;j++)

if(key[j]>0&&key[j]

v=j;//所以条件key[j]>0保证访问不会构成环

min=key[j];}

printf("%d%d",p[v],v);

key[v]=0;//访问过的点被置为0

for(j=1;j<=n;j++)

if(tb[v][j]

p[j]=v,key[j]=tb[v][j];

}

}

intmain()

{

intn,m;

inti,j;

intu,v,w;

while(scanf("%d%d",&n,&m))//输入顶点数,边数和某点到某点的权值

{

for(i=1;i<=n;i++)

{

for(j=1;j<=n;j++)

tb[i][j]=INT_MAX;

}

while(m--){

scanf("%d%d%d",&u,&v,&w);

tb[u][v]=tb[v][u]=w;

}

prim(1,n);

printf("\n");

}

return0;

}

Kruskal算法N城市遍历问题(依次选取最小边组成生成数)

#include

#include

#include

#definemax20

#defineMAX_LNT10

typedefstructnode/*构造一个结构体,两个城市可以看成起点和终点,之间的道路可以看成一个边*/

{

intstr;/*起点*/

intend;/*终点*/

intdis;/*距离*/

}node;

nodep[max],temp;/*p记录城市信息*/

intpre[100],rank[100];/*用于判断是否构成回路*/

intn=0,arcs[MAX_LNT][MAX_LNT];/*n表示城市个数,arcs[][]记录城市间权值*/

intmenu()/*菜单函数*/

{

intm;

printf("..........................2010年7月29日......................\n\n");

printf("求最小生成树\n");

printf("________________________________\n\n");

printf("1输入城市之间的信息\n");

printf("2判断是否能构成一个最小生成树\n");

printf("3遍历所有城市生成最小生成树\n");

printf("4退出\n");

printf("________________________________\n\n");

printf("请输入所选功能1-4\n");

system("colorE");/*改变界面颜色的,对程序没什么影响*/

scanf("%d",&m);

returnm;

}

/*下面三个函数作用是检验当一条边添加进去,是否会产生回路*/

voidset(intx)/*初始化*/11

{

pre[x]=x;

rank[x]=0;

}

intfind(intx)/*找到这个点的祖先*/

{

if(x!

=pre[x])

pre[x]=find(pre[x]);

returnpre[x];

}

voidUnion(intx,inty)/*将这两个添加到一个集合里去*/

{

x=find(x);

y=find(y);

if(rank[x]>=rank[y])

{

pre[y]=x;

rank[x]++;

}

elsepre[y]=x;

}

voidKruskal()

{

intans=0,i,j,k=0;/*ans用来记录生成最小树的权总值*/

intindex;

intcount=0;/*记录打印边的条数*/

for(i=1;i<=n;i++)/*初始化数组pre[x],rank[x]*/

set(i);

for(i=1;i<=n;i++)

{

for(j=i+1;j<=n;j++)

{

p[++k].str=i;

p[k].end=j;

p[k].dis=arcs[i][j];/*先把所有城市之间的路段看成一个边*/

}

}

for(i=1;i<=k;i++)/*把所有的边按从小到大进行排序*/

{

index=i;

for(j=i+1;j<=k;j++)if(p[j].dis

temp=p[index];

p[index]=p[i];

p[i]=temp;

}

for(i=1;i<=k;i++)

{

if(find(p[i].str)!

=find(p[i].end))/*如果这两点连接在一起不构成一个回路,

则执行下面操作*/

{

printf("\t第%d条路段为:

%d--%d,权值为%d\n",++

count,p[i].str,p[i].end,p[i].dis);/*将这条边的起点、终点打印出来*/

ans+=p[i].dis;/*说明这条路段要用*/

Union(p[i].str,p[i].end);

}

}

printf("\t遍历所有城市得到最小生成树的代价为:

%d\n\n",ans);

}

voidcreate()/*输入城市信息*/

{

inti,j;

printf("请输入城市的个数:

\n

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 历史学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1