1、acm备考资料整理by liangge背包问题P01: 01背包问题题目有N件物品和一个容量为V的背包。第i件物品的费用是ci,价值是wi。求解将哪些物品装入背包可使价值总和最大。基本思路这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。用子问题定义状态:即fiv表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:fiv=maxfi-1v,fi-1v-ci+wi先考虑上面讲的基本思路如何实现,肯定是有一个主循环i=1.N,每次算出来二维数组fi0.V的所有值。那么,如果只用一个数组 f0.V,能不能保证第i次循环结束后fv中表示的就是我们定义的状态f
2、iv呢?fiv是由fi-1v和fi-1 v-ci两个子问题递推而来,能否保证在推fiv时(也即在第i次主循环中推fv时)能够得到fi-1v和fi-1 v-ci的值呢?事实上,这要求在每次主循环中我们以v=V.0的顺序推fv,这样才能保证推fv时fv-ci保存的是状态 fi-1v-ci的值。伪代码如下:for i=1.Nfor v=V.0fv=maxfv,fv-ci+wi;其中的fv=maxfv,fv-ci一句恰就相当于我们的转移方程fiv=maxfi-1v,fi-1v-ci,因为现在的fv-ci就相当于原来的fi-1v-ci。如果将v的循环顺序从上面的逆序改成顺序的话,那么则成了fiv由fiv
3、-ci推知,与本题意不符,但它却是另一个重要的背包问题P02最简捷的解决方案,故学习只用一维数组解01背包问题是十分必要的。事实上,使用一维数组解01背包的程序在后面会被多次用到,所以这里抽象出一个处理一件01背包中的物品过程,以后的代码中直接调用不加说明。过程ZeroOnePack,表示处理一件01背包中的物品,两个参数cost、weight分别表明这件物品的费用和价值。procedure ZeroOnePack(cost,weight)for v=V.costfv=maxfv,fv-cost+weight初始化的细节问题我们看到的求最优解的背包问题题目中,事实上有两种不太相同的问法。有的题
4、目要求“恰好装满背包”时的最优解,有的题目则并没有要求必须把背包装满。一种区别这两种问法的实现方法是在初始化的时候有所不同。如果是第一种问法,要求恰好装满背包,那么在初始化时除了f0为0其它f1.V均设为-,这样就可以保证最终得到的fN是一种恰好装满背包的最优解。如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将f0.V全部设为0。为什么呢?可以这样理解:初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可 能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-
5、了。如果背包并非必须被装满,那么 任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。这个小技巧完全可以推广到其它类型的背包问题,后面也就不再对进行状态转移之前的初始化进行讲解。一个常数优化前面的伪代码中有 for v=V.1,可以将这个循环的下限进行改进。由于只需要最后fv的值,倒推前一个物品,其实只要知道fv-wn即可。以此类推,对以第j个背包,其实只需要知道到fv-sumwj.n即可,即代码中的for i=1.Nfor v=V.0可以改成for i=1.nbound=maxV-sumwi.n,cifor v=V.bound这对于V比较大时是有用
6、的。输入格式输入数据首先包含一个正整数C,表示有C组测试用例,每组测试用例的第一行是两个整数n和m(1=n=100, 1=m=100),分别表示经费的金额和大米的种类,然后是m行数据,每行包含3个数p,h和c(1=p=20,1=h=200,1=c=20),分别表示每袋的价格、每袋的重量以及对应种类大米的袋数。输出格式对于每组测试数据,请输出能够购买大米的最多重量,你可以假设经费买不光所有的大米,并且经费你可以不用完。每个实例的输出占一行。输入样例18 22 100 44 100 2输出样例400#include#includeusing namespace std;#define N 1005
7、#define Max(a,b) (a)(b)?(a):(b)#define Min(a,b) (a)t; while(t-) cinmn; for(i=1;ivolivalinni; memset(dp,0,sizeof(dp); for(i=1;i=0;j-) max=-1; for(k=0;k=nni&k*voli=j;k+) max=Max(max,dpi-1j-k*voli+k*vali); dpij=max; coutdpnmendl; return 0;下面为 0-1背包问题,上面为多重背包#include using namespace std;#define MAXSIZE
8、1000int fMAXSIZE + 1, cMAXSIZE + 1, wMAXSIZE + 1;int main()int N, V;cin V N;int i = 1;for (; i ci wi;for (i = 1; i = ci; -v) /ci可优化为bound, bound = max V - sum ci,.n, cifv = (fv fv - ci + wi ? fv : fv - ci + wi);/当i=N时,可以跳出循环单独计算FVcout fV n;return 0;背包问题,贪心法#include #include #include using namespace
9、std;int i,j,n;float W,x1000,sum;struct rm float w; float v;room1000;bool cmp(rm a,rm b) return a.v/a.wb.v/b.w;int main() cinWn; while(n!=-1 & W!=-1) for(i=0;iroomi.vroomi.w; sort(room,room+n,cmp); memset(x,0,1000); i=0; while(roomi.wW) xi=roomi.v; W=W-roomi.w; i+; xi=roomi.v/roomi.w*W; j=0; sum=0.0;
10、 while(j=i) sum+=xj; j+; coutsetiosflags(ios:fixed)setprecision(3)sumWn; return 0;输入样例5 37 24 35 2-1 -1(结束)输出样例13.333 对v/w从大到小排列,再依次选择八皇后问题#include#include #include using namespace std;struct node1bool b88; struct node2int x,y; node1 visited9;node2 zb8;int num;void print()printf(case%d: ,+num);for(i
11、nt i=0;i=7;i+)printf(%d,%dt,zbi.x,zbi.y);coutn;int x1,y1,x4,y4;void vis(int x,int y,int step) x1=x;y1=y;x4=x;y4=y;visitedstep=visitedstep-1;for(int i=0;i8;i+)visitedstep.bxi=0;visitedstep.biy=0;while(x18&y18)visitedstep.bx1y1=0;x1+;y1+;while(x4=0)visitedstep.bx4y4=0;x4+;y4-;void DFS(int step)if(step
12、=9) print();elsefor(int j=0;j8;j+)if(visitedstep-1.bstep-1j)zbstep-1.x=step-1;zbstep-1.y=j;vis(step-1,j,step);DFS(step+1);int main()num=0;memset(visited,1,sizeof(visited);DFS(1);/couthello worldendl;system(pause);站位问题:#include #include using namespace std; int counter=0; void display(int place) for(
13、int i=1;i=6;i+) coutplaceit; if(i%3=0) coutn; coutendl; bool judge(int mark,int place) switch(mark) case 1: return true; case 2: return place2!=5&place2!=6; case 3: return place3!=2&place3!=3; case 4: return place4!=1&place4!=2&place4!=3&place4!=6; case 5: return place5!=1&place5!=5&place5!=6; case
14、6: return place6!=1&place6!=6; bool others(int place) int m=1,n=1; for(int i=0;i=6;i+) if(placei=3) m=i-1; if(placei=4) n=i-1; return m/3!=n/3; void backtrace(int mark,int place,int people) for(int i=1;i=6;i+) if(peoplei!=0) placemark=peoplei; if(judge(mark,place) if(mark=6&others(place) counter+; d
15、isplay(place); else peoplei=0; backtrace(mark+1,place,people); peoplei=i; int main(int argc, char *argv) int *place=new int7; int *people=new int7; for(int i=1;i=6;i+) peoplei=i; backtrace(1,place,people); coutThe total methods is counterendl; system(PAUSE); return EXIT_SUCCESS; 把一个数分解为若干数的和#include
16、 #include #define MAXSIZE 50static int buffMAXSIZE;int Sum(int buff,int i)int sum=0;for(int m=0;m =i;m+)sum+=buffm;return sum;void printFactor(int buff,int i,const int number)int count=0,m;for(m=0;m 0;j-)bufftop=j;if(number=Sum(buff,top)printFactor(buff,top,number);else if(number =number)continue;di
17、vide(j,top+1,number);return;void main()int i,top=0;int number;printf( input a souce number: );scanf(%d,&number);i=number;divide(i,top,number);普里姆prim最小生成数#include #include #define N 100int pN, keyN, tbNN; /keyj表示到达j点的路径void prim(int v, int n) int i, j; int min; for (i = 1; i = n; i+) /从第一个顶点开始找 pi =
18、 v; keyi = tbvi; /初始化到达i点的各个值 keyv = 0; for (i = 2; i = n; i+) min = INT_MAX; for (j = 1; j 0 & keyj 0保证访问不会构成环 min = keyj; printf(%d%d , pv, v); keyv = 0; /访问过的点被置为0 for (j = 1; j = n; j+) if (tbvj keyj) /保证到达j点的值最小 pj = v, keyj = tbvj; int main() int n, m; int i, j; int u, v, w; while (scanf(%d%d,
19、 &n, &m) /输入顶点数,边数和某点到某点的权值 for(i = 1; i = n; i+) for (j = 1; j = n; j+) tbij = INT_MAX; while (m-) scanf(%d%d%d, &u, &v, &w); tbuv = tbvu = w; prim(1, n); printf(n); return 0;Kruskal算法 N城市遍历问题(依次选取最小边组成生成数)#include #include #include #define max 20 #define MAX_LNT 10 typedef struct node /*构造一个结构体,两个
20、城市可以看成起点和终点,之间的道路可以看成一个边*/ int str; /*起点*/ int end; /*终点*/ int dis;/*距离*/ node; node pmax,temp; /*p记录城市信息*/ int pre100,rank100;/*用于判断是否构成回路*/ int n=0,arcsMAX_LNTMAX_LNT;/*n表示城市个数,arcs记录城市间权值*/ int menu( ) /*菜单函数*/ int m; printf(.2010年7月29日.nn); printf( 求最小生成树n); printf( _nn); printf( 1 输入城市之间的信息n);
21、printf( 2 判断是否能构成一个最小生成树n); printf( 3 遍历所有城市生成最小生成树n); printf( 4 退出n); printf( _nn); printf( 请输入所选功能1-4n); system(color E);/*改变界面颜色的,对程序没什么影响*/ scanf(%d,&m); return m; /*下面三个函数作用是检验当一条边添加进去,是否会产生回路*/ void set(int x)/*初始化*/ 11 prex = x; rankx = 0; int find(int x)/*找到这个点的祖先*/ if(x != prex) prex = find
22、(prex); return prex; void Union(int x,int y)/*将这两个添加到一个集合里去*/ x = find(x); y = find(y); if(rankx = ranky) prey = x; rankx +; else prey = x; void Kruskal( ) int ans = 0,i,j,k = 0; /*ans用来记录生成最小树的权总值*/ int index; int count = 0; /*记录打印边的条数*/ for(i = 1;i = n;i +) /*初始化数组prex,rankx*/ set(i); for(i = 1;i
23、= n;i +) for(j = i + 1;j = n;j +) p+k.str = i; pk.end = j; pk.dis = arcsij; /*先把所有城市之间的路段看成一个边*/ for(i=1;i=k;i+) /*把所有的边按从小到大进行排序*/ index=i; for(j=i+1;j=k;j+) if(pj.dis pindex.dis) index=j; 12 temp=pindex; pindex=pi; pi=temp; for(i = 1;i = k;i +) if(find(pi.str) != find(pi.end)/*如果这两点连接在一起不构成一个回路,则执行下面操作*/ printf(t第%d条路段为:%d-%d,权值为%dn,+ count,pi.str,pi.end,pi.dis);/*将这条边的起点、终点打印出来*/ ans += pi.dis; /*说明这条路段要用*/ Union(pi.str,pi.end); printf(t遍历所有城市得到最小生成树的代价为: %dnn,ans); void create( ) /*输入城市信息*/ int i,j; printf(请输入城市的个数:n
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1