动态规划100例

内容来自:文档资料库http://www.03964.com/
更多"动态规划100例"相关资料请点击这里

一 资源问题.....................................................................................................................................4 资源问题 1 机器分配问题....................................................................................................4 资源问题 2 01 背包问题.......................................................................................................4 资源问题 3 系统可靠性(完全背包).....................................................................................9 资源问题 4 金明的预算方案:加花的动态规划 ..............................................................11 资源问题 5 化工场装箱员.................................................................................................15 资源问题 6 背包问题...........................................................................................................18 资源问题 7 装箱问题(判定性 01 背包).............................................................................18 资源问题 8 背包问题(+-1 背包问题+回溯)........................................................................ 18 二线性动态规划...........................................................................................................................18 线性动态规划 1 朴素最长非降子序列..............................................................................18 线性动态规划 2 日程安排 ....................................................................................................18 线性动态规划 3 组合数:..................................................................................................18 线性动态规划 4 多重历史..................................................................................................19 线性动态规划 5 最少单词个数............................................................................................19 线性动态规划 6 合唱队形..................................................................................................19 线性动态规划 7 决斗.........................................................................................................19 线性动态规划 8 舞蹈家......................................................................................................19 线性动态规划 9 (字符串)...................................................................................................19 线性动态规划 10 积木游戏..................................................................................................22 线性动态规划 11 打鼹鼠....................................................................................................22 线性动态规划 12 找数......................................................................................................23 线性动态规划 15 隐形的翅膀........................................................................................23 三 线型动态规划........................................................................................................................24 线型动态规划 1 方块消除游戏..........................................................................................24 线型动态规划 2 最长公共子串,LCS 问题......................................................................24 线型动态规划 3 IOI 2000 邮局问题.................................................................................24 线型动态规划 4 Vijos 1198 最佳课题选择.......................................................................26 线型动态规划 5 APIO2007 数据备份..............................................................................26 四 剖分问题...................................................................................................................................27 剖分问题 1 石子合并.........................................................................................................27 剖分问题 2 多边形剖分 ......................................................................................................39 剖分问题 3 乘积最大..........................................................................................................41 剖分问题 4 多边形-讨论的动态规划 ................................................................................43 剖分问题 5 最大奖励..........................................................................................................44 剖分问题 6 小 H 的小屋 .....................................................................................................45 贪心的动态规划.............................................................................................................................45 贪心的动态规划 1 快餐问题..............................................................................................45 贪心的动态规划 2(过河).......................................................................................................46 计数问题.........................................................................................................................................49 计数问题 1 砝码称重..........................................................................................................49 计数问题 2 陨石的秘密......................................................................................................50 最大子矩阵.....................................................................................................................................55 最大子矩阵 1 最大 01 子矩阵 ............................................................................................55


最大子矩阵 2 最大带权 01 子矩阵....................................................................................55 最大子矩阵 3 最大字段和问题............................................................................................56 最大子矩阵问题 4 最大子立方体问题..............................................................................56 最大子矩阵问题 5 居住空间..............................................................................................56 判定性问题.....................................................................................................................................56 判定性问题 1 能否被 4 整除..............................................................................................56 判定性问题 2 能否被 k 整除 ................................................................................................56 数字三角形.....................................................................................................................................56 数字三角形 1 朴素数字三角形 ............................................................................................57 数字三角形 2 晴天小猪历险记..........................................................................................57 数字三角形 3 小胖办证......................................................................................................57 数字三角形 4 过河卒............................................................................................................57 数字三角形 5 朴素的打砖块..............................................................................................57 数字三角形 6 优化的打砖块..............................................................................................57 状态压缩动态规划.........................................................................................................................58 状态压缩动态规划 1---炮兵阵地..........................................................................................58 递推天地.........................................................................................................................................59 递推天地 1 核电站问题......................................................................................................59 递推天地 2 ------数的划分..................................................................................................60 .递推天地 3 情书抄写员........................................................................................................62 递推天地 4 错位排列..........................................................................................................64 递推天地 5 直线分平面最大区域数..................................................................................64 递推天地 6 折线分平面最大区域数..................................................................................64 递推天地 7 封闭曲线分平面最大区域数.......................................................................... 64 递推天地 8 凸多边形分三角形方法数..............................................................................64 递推天地 9 Catalan 数列一般形式.....................................................................................64 递推天地 10 彩灯布置........................................................................................................64 递推天地 11-----盒子与球..................................................................................................65 树型动态规划.................................................................................................................................66 树型动态规划 1 加分二叉树..............................................................................................66 树型动态规划 2 选课 ..........................................................................................................70 树形动态规划 3 贪吃的九头龙..........................................................................................74 树形动态规划 4 有线电视网................................................................................................79 树形动态规划 5 有向树 k 中值问题....................................................................................79 树形动态规划 6 聚会的快乐..............................................................................................79 树形动态规划 7 .皇宫看守.................................................................................................79 树形动态规划 8 -----APIO2007 风铃...................................................................................80 树形动态规划 9-----CTSC 2001 选课...................................................................................80 树形动态规划 10(双次记录)............................................................................................81 树形动态规划 11(完全二叉树) NOI2006 网络收费 ........................................................... 83 树形动态规划 12 千年虫.......................................................................................................85 树形动态规划 13 IOI2005 河流...........................................................................................87 树形动态规划 14 -----访问术馆............................................................................................88 地图动态规划.................................................................................................................................89


地图动态规划 1 NOI 2005 adv19910...................................................................................89 地图动态规划-2----优化的 NOI 2005adv19910 .................................................................. 89 地图动态规划 3 vijos 某题...............................................................................................89 目标动态规划.................................................................................................................................90 目标动态规划 1 CEOI98 subtra.............................................................................................90 目标动态规划 2 Vijos 1037 搭建双塔问题....................................................................... 90 概率动态规划.................................................................................................................................90 概率动态规划 1-----聪聪和可可(NOI2005)......................................................................... 90 概率动态规划 2 血缘关系 ..................................................................................................90 其他类型.........................................................................................................................................91 1 记忆化搜索..........................................................................................................................91 2 状态压缩动态规划..............................................................................................................91 3 字符串动态规划..................................................................................................................96 4 多进程动态规划.................................................................................................................96 5 多进程动态规划.................................................................................................................96 6 多进程动态规划..................................................................................................................96 7 括号序列.............................................................................................................................97 8 棋盘切割.............................................................................................................................97 9 最短路 1............................................................................................................................100 10 双重动态规划.................................................................................................................101 11 动态规划.........................................................................................................................101 12 动态规划.........................................................................................................................101 13 动态规划.........................................................................................................................101 14 动态规划.........................................................................................................................101 15 动态规划.........................................................................................................................101 16 动态规划.........................................................................................................................102 17 动态规划.........................................................................................................................102 18 动态规划.........................................................................................................................102 19 描述 Description.............................................................................................................103 20、描述 Description..........................................................................................................103


一 资源问题
资源问题 1 机器分配问题 ----总公司拥有高效生产设备 M 台,准备分给下属的 N 个公司。各分公司若获得这些设备, 可以为国家提供一定的盈利。问: 如何分配这 M 台设备才能使国家得到的盈利最大?求出最 大盈利值。其中M<=15,N<=10。分配原则:每个公司有权获得任意数目的设备,但总台数 不得超过总设备数 M。 数据文件格式为:第一行保存两个数,第一个数是设备台数 M,第二个数是分公司数 N。接下来是一个 M*N 的矩阵,表明了第 I 个公司分配 J 台机器的盈利。 用机器数来做状态,数组 F[I,J]表示前 I 个公司分配 J 台机器的最大盈利。 则状态转移方程为:F[I,j]:=max(f[i-1,k]+w[i,j-k]) 资源问题 2 01 背包问题

F[I,j]:=max(f[i-1,j-v[i]]+w[i],f[i-1,j]); * 一个旅行者有一个最多能用 M 公斤的背包,现在有 N 件物品, 它们的重量分别是 W1,W2,...,Wn, 它们的价值分别为 P1,P2,...,Pn. 若每种物品只有一件求旅行者能获得最大总价值。 输入格式:M,N W1,P1 W2,P2 ...... 输出格式: X */ 因为背包最大容量 M 未知。所以,我们的程序要从 1 到M 一个一个的试。比如,开 始任选 N 件物品的一个。看对应M 的背包,能不能放进去,如果能放进去,并且还有多的空 间,则,多出来的空间里能放 N-1 物品中的最大价值。怎么能保证总选择是最大价值呢?看下表。 测试数据: 10,3 3,4 4,5 5,6


c[i][j]数组保存了 1,2,3 号物品依次选择后的最大价值. 这个最大价值是怎么得来的呢?从背包容量为 0 开始,1 号物品先试,0,1,2,的容量都不能放.所以置 0,背包容量为 3 则里面放 4.这样,这一排背包容量为 4,5,6,....10 的时 候,最佳方案都是放 4.假如 1 号物品放入背包.则再看 2 号物品.当背包容量为 3 的时候, 最佳方案还是上一排的最价方案 c 为 4.而背包容量为 5 的时候,则最佳方案为自己的重量 5.背包容量为 7 的时候, 很显然是5 加上一个值了。 加谁??很显然是 7-4=3 的时候.上一排 c3 的最佳方案是 4.所以。总的最佳方案是 5+4 为 9.这样.一排一排推下去。 最右下放的数据就 是最大的价值了。(注意第 3 排的背包容量为 7 的时候,最佳方案不是本身的 6.而是上一排 的 9.说明这时候 3号物品没有被选.选的是 1,2 号物品.所以得 9.) 从以上最大价值的构造过程中可以看出。 f(n,m)=max{f(n-1,m), f(n-1,m-w[n])+P(n,m)}这就是书本上写的动态规划方程. 这回清楚了吗? 下面是实际程序:#include<stdio.h> int c[10][100];/*对应每种情况的最大价值*/int knapsack(int m,int n) { int i,j,w[10],p[10]; for(i=1;i<n+1;i++)scanf("\n%d,%d",&w[i],&p[i]); for(i=0;i<10;i++)for(j=0;j<100;j++) c[i][j]=0;/*初始化数组*/for(i=1;i<n+1;i++) for(j=1;j<m+1;j++) { if(w[i]<=j) /*如果当前物品的容量小于背包容量*/ { if(p[i]+c[i-1][j-w[i]]>c[i-1][j]) /*如果本物品的价值加上背包剩下的空间能放的物品的价值*/


/*大于上一次选择的最佳方案则更新 c[i][j]*/c[i][j]=p[i]+c[i-1][j-w[i]]; else c[i][j]=c[i-1][j]; } else c[i][j]=c[i-1][j];} return(c[n][m]);

} int main() { int m,n;int i,j; scanf("%d,%d",&m,&n);printf("Input each one:\n"); printf("%d",knapsack(m,n));printf("\n");/*下面是测试这个数组,可删除*/ for(i=0;i<10;i++) for(j=0;j<15;j++) { printf("%d",c[i][j]); if(j==14)printf("\n"); } system("pause");}

//程序名称:动态规划解 0-1 背包问题#include<iostream> #include<iomanip> using namespace std;

const int MAX=1000; int w[MAX],v[MAX],best[MAX]; int V[MAX][MAX]; //最大价值矩阵 int W,n; //W 为背包的最大载重量,n 为物品的数量


//求最大值函数 int max(int x,int y) { return x >= y?x:y; }

//求最小值函数 int min(int x,int y) { return x>= y ? y:x;}

void Knaspack() { int Max=min(w[n]-1,W); for(int j=1; j <= Max ; j++)V[n][j]=0; for( int j=w[n]; j <= W ; j++) V[n][j]=v[n]; for(int i=n-1;i >1 ; i--) { Max=min(w[i]-1,W); for(int j=1; j <= Max ; j++)V[i][j]=V[i+1][j];

for(int j=w[i]; j <= W; j++) V[i][j]=max(V[i+1][j],V[i+1][j-w[i]]+v[i]); }V[1][W]=V[2][W]; //先假设第一个物品不放入 if(W > w[1])V[1][W]=max(V[1][W],V[2][W-w[1]]+v[1]); }

//生成向量数组,决定某一个物品是否应该放入背包


void Traceback() { for(int i=1; i < n ; i++) 的最优值. {if(V[i][W] == V[i+1][W]) 等,则表明该物品不能放入。 best[i]=0; else{ best[i]=1; W-=w[i]; } } best[n]=(V[n][W] )?1:0; } //否则可以放入 //如果当前行的最优值与下一行的最优值相 //比较矩阵两邻两行(除最后一行),背包容量为 W

void main() { cout<<"输入商品数量 n 和背包容量 W:"; cin>>n>>W;

cout<<"输入每件商品的重量 w:"<<endl;for(int i=1;i<=n;i++) cin>>w[i]; cout<<"输入每件商品的价值 v:"<<endl; for(int i=1;i<=n;i++) cin>>v[i];memset(V,0,sizeof(V)); Knaspack();//构造矩阵 Traceback();//求出解的向量数组

int totalW=0;


int totalV=0; //显示可以放入的物品 cout<<"所选择的商品如下:"<<endl; cout<<"序号 i:重量 w:价格 v:"<<endl;

for(int i=1; i <= n ; i++) { if(best[i] == 1) { totalW+=w[i]; totalV+=v[i];cout<<setiosflags(ios::left)<<setw(5)<<i<<""<<v[i]<<endl; } } cout<<"放入的物品重量总和是:"<<totalW<<" 价值最优解是:"<<V[1][W]<<""<<totalV<<endl; } 资源问题 3 系统可靠性(完全背包) "<<w[i]<<"

有 N 种物品和一个容量为 V 的背包,每种物品都有无限件可用。第 i 种物品的 费用是 c,价值是 w。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

F[i,j]:=max{f[i-1,j-c[i]*k]*P[I,x]} 系统可靠性 一个 系统由若干部件串联而成, 只要有一个部件故障, 系统就不能正常运行,为 提高系统的可靠性, 每一部件都装有备用件, 一旦原部件故障, 备用件就自 动进入系统。 显然备用件越多, 系统可靠性越高,但费用也越大, 那么在一 定总费用限制下,系统的最高可靠性等于多少? 给定一些系统备用件的单价 Ck, 以及当用 Mk个此备用件时部件的正常工作概率 Pk(Mk) ,总费用 上限 C。 求系统可能的最高可靠性。 输入文件格式:第一行: n C第二行: C1 P1 (0) P1 (1 ) ? P1 (X1 ) (0<=X1 <=[C/Ck] ) ? 第 n 行: Cn Pn(0) Pn(1 ) ? Pn(Xn) (0<=Xn<=[C/Cn] )分析 例: 输入: 2 20 3 0 . 60.65 0.7 0.75 0.8 0. 85 0. 9 5 0.7 0.75 0.8 0.8 0.9 0. 95 输出: 0.6375 设 F[I, money] 表示将 money 的资金用到前 I 项备用件中去的最大可靠 性, 则有 F[I, money] = max{F[I-1 , money�Ck*Cost[I] ] *P[I,


k] } (0<=I<=n, 0<=K<= money div Cost(I) ) 初始: F[0, 0] =0 目标: F[n, C]
1.证明这个问题符合最优化原理。可以用反证法证明之。假设用 money 的资金购买了前 I 项备用件,得到了最高的系统可靠性,而且又存在如下情况:对于备用件 I,设要买 Ki 个, 则可用的资金还剩余 money �C Ci*Ki,用这些钱购买前 (I-1) 项备用件, 如果存在一种前 (I-1)种备用件的购买方案得到的系统可靠性比当前得到的要高, 那么这个新的方案会使得整个前 I 项备用件组成的系统可靠性比原先的更高,与原假设矛盾。所以可以证明这个问题符合最优化原理。 2.证明这个问题符合无后效性原则。 3.综上所述,本题适合于用动态规划求解。 4.递推方程及边界条件: F[I,money] := max {F[I-1,money �C C[I]*K[I] ] } (0<=K[I]<= C div Ci ) 三、参考程序
{$Q-,R-,S-} {$M 16384,0,655360} Program system_dependability; constfinp='input.txt'; fout='output.txt'; maxm=3000; var f,p:array[0..maxm] of real;max,v:double; c,co,e,i,j,k,m,n:integer; procedure print; var output:text; beginassign(output,fout); rewrite(output); writeln(f[c]:1:4); close(output); end;Begin assign(input,finp); reset(input); readln(input,n,c); for i:=0 to c dof[i]:=1; for i:=1 to n do begin read(input,co); m:=c div co; for e:=0 to m doread(input,p[e]); for j:=c downto 0 do begin m:=j div co; max:=0; for k:=0 to mdo begin v:=f[j-k*co]*p[k]; if v>max then max:=v; end; f[j]:=max;


end; end; close(input); print End.

资源问题 4

-金明的预算方案

金明的预算方案 (budget.pas/c/cpp) 【问题描述】 金明今天很开心, 家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房 间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪 些物品,怎么布置,你说了算,只要不超过 N 元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下 表就是一些主件与附件的例子: 主件 附件 电脑 打印机,扫描仪 书柜 图书 书桌 台灯,文具 工作椅 无 如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 0 个、1 个或 2 个附件。附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的 N 元。于是,他把每件物品规定了一个重要度,分为 5 等:用整数 1~5 表示,第 5 等最重要。 他还从因特网上查到了每件物品的价格(都是 10 元的整数倍) 。他希望在不超过 N 元(可 以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。 设第 j 件物品的价格为 v[j],重要度为 w[j],共选中了k 件物品,编号依次为 j1,j2,⋯⋯, jk,则所求的总和为: v[j1]*w[j1]+v[j2]*w[j2]+ ⋯+v[jk]*w[jk]。 (其中*为乘号) 请你帮助金明设计一个满足要求的购物单。 【输入文件】 输入文件budget.in 的第 1 行,为两个正整数,用一个空格隔开: N m (其中 N(<32000)表示总钱数,m(<60)为希望购买物品的个数。 ) 从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q (其中 v 表示该物品的价格(v<10000) 表示该物品的重要度(1~5) 表示该物品是主 ,p ,q 件还是附件。如果 q=0,表示该物品为主件,如果 q>0,表示该物品为附件,q 是所属主件 的编号) 【输出文件】 输出文件 budget.out 只有一个正整数, 为不超过总钱数的物品的价格与重要度乘积的总和的 最大值(<200000) 。 【输入样例】 1000 5 800 2 0


400 5 1 300 5 1 400 3 0 500 2 0 【输出样例】 2200

f[i,j]:=max(f[i,j],f[l,j-v-v[fb]-v[fa]]+v*p+v[fb]*p[fb]+v[fa]*p [fa]);
如果看过普及组试卷就会发现,对应的第二个题目,也是一个样的背景,提高组只是 多了个“主件附件”的的关系,如果去掉这一点,就全没区别了。也就成了经 典的背包问题 了。也就是多了这么一点,考试的时候就晕了。不知道怎么做了。后来才发现是个很简单的 dp 题目。可惜我当时没做出来。 草率的审题,可能会得到这样的算法:dp,对每一个物品做两种决策,取与不取。如果取,满足两个条件:1.要么它是主件,要么它所属的主件已经在包里了。2.放进去后的重要度与价格的成绩的总和要比没放进时的大。这两个条件缺一不可的。于是呼,得到如下的动 规方程:f[i,j]:=f[i-1,j]; if (i 为主件 or i 的附件在包中)and (f[i,j]<f[i,j-v]+v*w) then f[i,j]:=f[i,j-v]+v*w; 我们来分析一下复杂度,空间:dp 的阶段为 n^2,对与每一个阶段都要记录该状态下在 包中的物品有哪些(因为要确定附件的主件是否在包中), 每个阶段的 记录都要 O(n)的空间,所以总的就是 O(n^3)。时间,一个 dp,n^2 的外层循环,内部用布尔量加个主附件的对应 数组,为 O(1),和起来就为 O(n^2)的复杂度。 可以看的出,时间的需求为 32000*60,不成问题。空间 32000*60*60,大约要 7.5M 的 空间,在 64M 的要求下是完全可以的过的。如果用上题目中的一个很隐秘的条件:“每件物品都是 10 元的整数倍”,就可以把速度在提高十倍。 细细的看题目,还一个很重要的条件我们还没用:“每个主件可以有0个,1个或2个 附件”。这貌似不起眼的一句话,却给我们降低复杂度提供了条件。想一想,为什么题目要对附件的个数做限制呢,明显是在降低难度。 对于一套物品(包含主件,所以的附件) ,我们称为一个属类,对一个属类的物品的购 买方法,有以下5种: 1.一个都不买 2.主件 3.主件+附件14.主件+附件2 5.主件+附件1+附件2 这五种购买方法也是唯一的五种方法,也就是说对一属类的物品, 我们只有上述的5种 购买方法。 于是我们很自然的就会想到把物品按物品的属类捆在一起考虑。 这样我们把物品的属类 作为 dp 的状态。可以得到如下的 dp 方程: f[i,j]=max{f[i-1,j];


f[i-1,j-v[i,0]]+v[i,0]*w[i,0];f[i-1,j-v[i,0]-v[i,1]]+v[i,0]*w[i,0]+v[i,1]*w[i,1];f[i-1,j-v[i,0]-v[i,2]]+v[i,0]*w[i,0]+v[i,2]*w[i,2]; f[i-1,j-v[i,0]-v[i,1]-v[i,2]]+v[i,0]*w[i,0]+v[i,1]*w[i,1]+v[i,2]*w[i,2];}很显然时间复杂度为 O(n^2),空间复杂度为 O(n^2),加上利用“每件物品都是 10 元的 整数倍”除以 10 的优化,本题就很完美的解决了。 (附程序) ; programQi(input,output); type node=record u:integer; v:array[0..2] of integer;p:array[0..2] of integer; end; var n,m,k:integer; w:array[1..60] of node;f:array[0..60,0..3200] of longint; g:array[1..60] of integer; procedure init;var i,j:integer; vx,px,qx:array[1..60] of integer; begin readln(n,m); k:=0; fori:=1 to m do begin readln(vx,px,qx); if qx=0 then begin k:=k+1; g:=k; with w[k]do begin u:=0; v[0]:=vx; p[0]:=px; for j:=1 to 2 do begin v[j]:=0; p[j]:=0;end; end; end; end; for i:=1 to m do if qx<>0 then begin with w[g[qx]] do


begin u:=u+1; v:=vx; p:=px; end; end; for i:=1 to k do with w do begin for j:=0to 2 do write('(',v[j],',',p[j],') '); writeln; end; end; procedure doit; vari,j:integer; begin fillchar(f,sizeof(f),0); for i:=1 to k do with w do beginfor j:=1 to n do begin f[i,j]:=f[i-1,j]; if (j>=v[0]) and(f[i,j]<f[i-1,j-v[0]]+v[0]*p[0]) then f[i,j]:=f[i-1,j-v[0]]+v[0]*p[0]; if(j>=v[0]+v[1]) and (f[i,j]<f[i-1,j-v[0]-v[1]]+v[0]*p[0]+v[1]*p[1]) thenf[i,j]:=f[i-1,j-v[0]-v[1]]+v[0]*p[0]+v[1]*p[1]; if (j>=v[0]+v[2]) and(f[i,j]<f[i-1,j-v[0]-v[2]]+v[0]*p[0]+v[2]*p[2]) then f[i,j]:=f[i-1,j-v[0]-v[2]]+v[0]*p[0]+v[2]*p[2];if (j>=v[0]+v[1]+v[2]) and(f[i,j]<f[i-1,j-v[0]-v[1]-v[2]]+v[0]*p[0]+v[1]*p[1]+v[2]*p[2]) thenf[i,j]:=f[i-1,j-v[0]-v[1]-v[2]]+v[0]*p[0]+v[1]*p[1]+v[2]*p[2]; end; end; end;procedure print; begin writeln(f[k,n]); end; begin init; doit; print;


end.

资源问题 5 化工场装箱员 118号工厂是世界唯一秘密提炼锎的化工厂,由于提炼锎的难度非常高, 技术不是十分完善, 所以工厂生产的锎成品可能会有3种不同的纯 度,A:100%,B:1%,C:0.01%,为了出售方便,必须把不同纯度的成品分开装箱,装箱员 grant 第1次顺序从流水线上取10个成品 (如果一 共不足10个,则全部取出) ,以后每一次把手中某种纯度的成品放进相应的箱子,然后再从流水线上顺序取一些成品,使手中保持10个成品(如果把剩下的全部 取出不足10 个,则全部取出) ,如果所有的成品都装进了箱子,那么 grant 的任务就完成了。 ?? 由于装箱是件非常累的事情,grant 希望他能够以最少的装箱次数来完成他的任务,现在他请你编个程序帮助他。 【输入格式】 第1行为 n(1<=n<=100) ,为成品的数量 以后 n 行,每行为一个大写字母 A,B 或 C,表示成品的纯度。【输出格式】 仅一行,为 grant 需要的最少的装箱次数。 【输入样例】worker.in11 A B C A B C A B C A B 【输出样例】worker.out 3 viewsourceprint? 01 //动态规划 02 #include<cstdio> 03#include<cstdlib> 04 intn,i; 05 intf[150][11][11][11]; 06boolg[150][11][11][11]; 07 intdata[150]; 08 09 intmin(inta,intb) 10 {


11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

if(a<b)returna;elsereturnb; } intdp(intk,inta,intb,intc) { inti,j=a+b+c;for(i=1;i<=10-j;i++) { k++; if(k>n)break; if(data[k]==1) a++;if(data[k]==2) b++; if(data[k]==3) c++; } if(k>=n) { g[n][a][b][c]=1;f[n][a][b][c]=(a>0)+(b>0)+(c>0); returnf[n][a][b][c]; }if(!g[k][a][b][c]) { g[k][a][b][c]=1; f[k][a][b][c]=100000;


35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

if(a>0) f[k][a][b][c]=min(f[k][a][b][c],dp(k,0,b,c)+1); if(b>0)f[k][a][b][c]=min(f[k][a][b][c],dp(k,a,0,c)+1); if(c>0) f[k][a][b][c]=min(f[k][a][b][c],dp(k,a,b,0)+1);} returnf[k][a][b][c]; }

intmain() { freopen("worker.in","r",stdin);//freopen("worker.out","w",stdout);scanf("%d\n",&n); charc; for(i=1;i<=n;i++) {scanf("%c\n",&c); data[i]=c-64; } intans=dp(0,0,0,0); printf("%d\n",ans);//system("pause"); return0; }


资源问题 6 背包问题

----- USACO Raucous Rockers 多个背包,不可以重复放物品,但放物品的顺序有限制。F[I,j,k]表示决策到第 i 个物品、第 j 个背包,此背包花费了 k 的空间。f[I,j,k]:=max(f[I-1,j,k],f[I-1,j,k-t]+p,f[i-1,j-1,maxtime-t])

资源问题 7 装箱问题(判定性 01 背包)
f[j]:=(f[j] or f[j-v]);

资源问题 8 背包问题(+-1 背包问题+回溯)

-----CEOI1998 Substract f[i,j]:=f[i-1,j-a] or f[i-1,j+a]

二 线性动态规划
线性动态规划 1 朴素最长非降子序列

设有由 n 个不相同的整数组成的数列, 记为: a(1),a(2),??,a(n)且 a(i)<>a(j) (i<>j) 例如 3,18,7,14,10,12,23,41,16,24。 若存在 i1< ? < ie 且有 a(i1)< ? 则称为长度为 e 的不下降序列。如上例中 3,18,23,24 就是一个长度为 4 的不下降序列,同时也有 3,7,10,12,16, 24 长度为 6 的不下降序列。程序要求,当原数列给出之后,求出最长的不下降 序列。F[i]:=max{f[j]+1}
线性动态规划 2 日程安排

-----f:=max{f[j]}+P[I]; (e[j]<s)
线性动态规划 3 组合数:
描述 Description 组合公式C=N!/(M!*(N-M)!). 问题是求 C 中不同的质因子的个数 例如 N=7, M=4. C=7!/(3!*4!)=5040/(6*24)=35=5*7. 则不同的质因子的个数为 2 (分别是 5,7)。


输入格式 Input Format 输入 N,M ??(1 <= N, M <= 50000) 输出格式 OutputFormat 输出一个整数 样例输入 Sample Input

7 4
样例输出 Sample Output

2

线性动态规划 4 多重历史

----f[i,j]:=sigma{f[i-k,j-1]}(if checked)

线性动态规划 5 最少单词个数

-----最少单词个数 f[i,j]:=max{f[I,j],f[u-1,j-1]+l}

线性动态规划 6 合唱队形

------合唱队形 两次 F:=max{f[j]+1}+枚举中央结点

线性动态规划 7 决斗

-----决斗 F[I,j]=(f[I,j] and f[k,j]) and (e[I,k] ore[j,k]),i<k<j

线性动态规划 8 舞蹈家

F[x,y,k]=min(f[a[k],y,k+1]+w[x,a[k]],f[x,a[k],k+1]+w[y,a[k]])
线性动态规划 9 (字符串)

-----NOI 2000 古城之谜古城之谜


著名的考古学家石教授在云梦高原上发现了一处古代城市遗址。 让教授欣喜的是在这个他称为冰 峰城(Ice-PeakCity)的城市中有 12 块巨大石碑,上面刻着用某种文字书写的资料,他称这种文 字为冰峰文。然而当教授试图再次找到冰峰城时,却屡屡无功而返。幸好当时教授把石碑上的文字都拍摄了下来, 为了解开冰峰城的秘密, 教授和他的助手牛博士开 始研究冰峰文,发现冰峰文只有陈述句这一种句型和名词(n)、动词(v)、辅词(a)这三类单词,且其文法很简单: ? <文章> ::= <句子> { <句子> } ? <句子> ::= <陈述句> ? <陈述句> ::= <名词短语> { <动词短语> <名词短语> } [ <动词短语> ] ? <名词短语> ::= <名词> | [ <辅词> ] <名词短语> ? <动词短语> ::= <动词> | [ <辅词> ] <动词短语> ? <单词> ::= <名词> | <动词> | <辅词> 注:其中<名词>、<动词>和<辅词>由词典给出,“::=”表示定义为,“|”表示或,{}内的项可以重 复任意多次或不出现,[]内的项可以出现一次或不出现。 在研究了大量资料后,他们总结了一部冰峰文词典,由于冰峰文恰好有 26 个字母,为了研究方 便,用字母 a 到 z 表示它们。 冰峰文在句子和句子之间以及单词和单词之间没有任何分隔符, 因此划分单词和句子令石教授和 牛博士感到非常麻烦,于是他们想到了使用计算机来帮助解决这个问题。假设你接受了这份工 作,你的第一个任务是写一个程序,将一篇冰峰文文章划分为最少的句子,在这个前提下,将文 章划分为最少的单词。 [输入文件] ? 输入文件第 1 行为词典中的单词数 n(n<=1000)。 ? 输入文件第 2 行至第(n+1)行每行表示一个单词,形为“α.mot”, α 表示词性,可 能是n(名词),v(动词),a(辅词)中的一个,mot 为单词,单词的长度不超过 20。拼写相同而词性不同的单词视为不同的单词,如输入示例中的 n.kick 与 v.kick 是两个不同的单词。 ? 输入文件第(n+2)行为需要划分的文章,以“.”结束。 ? 输入文件中的文章确保为冰峰文。文章是由有限个句子组成的,每个句子只包含有限个单词。文章长度不超过 5KB。 [输出文件]


? 输出文件为两行,每行一个整数。 ? 输出文件第 1 行为划分出来的句子数。 ? 输出文件第 2 行为划分出来的单词数。 [输入输出文件样例] Input

11 n.table n.baleine a.silly n.snoopy n.sillysnoopy v.is v.isnot n.kick v.kicka.big v.cry sillysnoopyisnotbigtablebaleinekicksnoopysillycry.
Output

2 9
[样例说明] (为了阅读方便,划分的单词用空格分隔,在单词右标出它的词性,每行写一个句子,用句号表示句子结束。) 输出对应的划分:

sillysnoopy[n] isnot[v] big[a] table[n]. baleine[n] kick[v] snoopy[n] silly[a]cry[v].
如果用下面的划分:

silly[a] snoopy[n] isnot[v] big[a] table[n].


baleine[n] kick[v] snoopy[n] silly[a] cry[v].
则划分的句子数仍为 2 个,但单词数却多了 1 个,为 10 个,显然应该按前者而不是后者划分。

线性动态规划 10 积木游戏

-----积木游戏F[I,a,b,k]=max(f[I,a+1,b,k],f[i+1,a+1,a+1,k’],f[I,a+1,a+1,k’]) 积木游戏(NOI’97) 一种积木游戏,游戏者有 N 块编号依次为 1,2,⋯,N 的长方体积木。第 I 块积 木通过同一顶点三条边的长度分别为 ai,bi,ci(i=1,2,⋯,N),如图 1 所示:

游戏规则如下: 1 从 N 块积木中选出若干块,并将他们摞成 M(1<= M <= N)根柱子,编号依次 为 1,2,⋯,M,要求第 k 根柱子的任意一块积木的编号都必须大于第 K-1 根柱子 任意一块积木的编号(2<=K<=M)。 2 对于每一根柱子,一定要满足下面三个条件: 除最顶上的一块积木外, 任意一块积木的上表面同且仅同另一块积木的下表面接 触;对于任意两块上下表面相接触的积木,若 m,n 是下面一块积木接触面的两条边 (m>=n),x,y 是上面一块积木接触面的两条边(x>=y),则一定满足 m.>=x 和 n>=y; 下面的积木的编号要小于上面的积木的编号。 请你编一程序, 寻找一种游戏方案, 使得所有能摞成的 M 根柱子的高度之和最大。 输入数据: 文件的第一行是两个正整数 N 和 M(1<= M <= N <=100),分别表示积木总数和 要求摞成的柱子数。这两个数之间用一个空格符隔开。接下来的 N 行是编号从 1 到 N 个积木的尺寸,每行有三个1 至 500 之间的整数,分别表示该积木三条边 的长度。同一行相邻两个数之间用一个空格符隔开。输出数据: 文件只有一行,是一个整数,表示所求得的游戏方案中 M 根柱子的高度之和。

线性动态规划 11 打鼹鼠
-----打鼹鼠’描述 Description 根据这个特点阿 Q 编写了一个打鼹鼠的游戏:在一个 n*n 的网格中,在某些时刻鼹鼠会在某一个网格探出头来透透气。你可以控制一个机器人来打鼹鼠,如果 i 时刻鼹鼠在某个网格中出现,而机器人也处于同一 网格的话,那么这个鼹鼠就会被机器人打死。而机器人每一时刻只能够移动一格或停留在原地不动。机器人的移动是指从当前所处的网格移向相邻的网格, 即从坐标为 (i,j) 的网格移向(i-1, j),(i+1, j),(i,j-1),(i,j+1) 四个网格,机器人不能走出整个 n*n 的网格。游戏开始时,你可以自由选定机器人的初始位置。


现在你知道在一段时间内,鼹鼠出现的时间和地点,希望你编写一个程序使机器人在这一段时间内打死尽 可能多的鼹鼠。 输入格式 Input Format 文件第一行为 n(n<=1000), m(m<=10000),其中 m 表示在这一段时间内出现的鼹鼠的个数,接下 来的 m 行每行有三个数据 time,x,y 表示有一只鼹鼠在游戏开始后 time 个时刻,在第 x 行第 y 个网格里出 现了一只鼹鼠。Time 按递增的顺序给出。注意同一时刻可能出现多只鼹鼠,但同一时刻同一地点只可能出 现一只鼹鼠。 输出格式 Output Format 输出文件中仅包含一个正整数,表示被打死鼹鼠的最大数目。 样例输入 Sample Input

2 2 1 1 1 2 2 2
样例输出 Sample Output

1
线性动态规划 12 找数

-----找数 线性扫描 sum:=f+g[j]; (if sum=Aim then getout; ifsum<Aim then inc(i) else inc (j);)

线性动态规划 15
背景 Background

隐形的翅膀

——隐形的翅膀
小杉终于进入了天堂。他看到每个人都带着一双隐形翅膀,他也想要。 (小杉是怎么看到的?⋯⋯) 描述 Description 天使告诉小杉,每只翅膀都有长度,两只翅膀的长度之比越接近黄金分割比例,就越完美。现在天使给了小杉 N 只翅膀,小杉想挑出一对最完美的。 输入格式 InputFormat 每组测试数据的 第一行有一个数 N(2<=N<=30000) 第二行有 N 个不超过 1e5 的正整数,表示N 只翅膀的长度。 20%的数据 N<=100 输出格式 Output Format 对每组测试数据输出两个整数,分两行,表示小杉挑选出来的一对翅膀。 注意,比较短的在前,如果有多对翅膀的完美程度一样,请输出最小的一对。样例输入 Sample Input

4


2 3 4 6
样例输出 Sample Output

2 3 三 线型动态规划

线型动态规划 1 方块消除游戏

-----方块消除游戏 f[i,i-1,0]:=0f[i,j,k]:=max{f[i,j-1,0]+sqr(len(j)+k), f[i,p,k+len[j]]+f[p+1,j-1,0]} ans:=f[1,m,0]

线型动态规划 2 最长公共子串,LCS 问题

-----最长公共子串,LCS 问题f[i,j]={0(i=0)&(j=0); f[i-1,j-1]+1 (i>0,j>0,x=y[j]);max{f[i,j-1]+f[i-1,j]}} (i>0,j>0,x<>y[j]);

线型动态规划 3 IOI 2000 邮局问题 [问题描述] 按照递增顺序给出一条直线上坐标互不相同的 n 个村庄, 要求从中选择 p 个村庄建立邮 局, 每个村庄使用离它最近的那个邮局, 使得所有村庄到各自所使用的邮局的距离总和最小。 试编程计算最小距离和,以及邮局建立方案。 [算法分析] 本题也是一道动态规划问题,详细分析请看文本附件(邮局解题报告)。将 n 个村庄按 坐标递增依次编号为 1,2,⋯⋯,n,各个邮局的坐标为 d[1..n],状态表示描述为:m[i,j] 表示在前 j 个村庄建立i 个邮局的最小距离和。所以,m[p,n]即为问题的解,且状态转移 方程和边界条件为: m[1,j]=w[1,j] i≤j 其中 w[i,j]表示在 d[i..j]之间建立一个邮局的最小距离和,可以证明,当仅建立一个 邮 局 时 , 最 优 解 出 现 在 中 位 数 ,即 设 建 立 邮 局 的 村 庄 为 k , 则
i ?1? k ? j ?1

m[i, j ] ? min {m[i ? 1, k ] ? w[k ? 1, j ]}

k ? ?(i ? j ) / 2?或k ? ?(i ? j ) / 2? ,于是,我们有:


, k ? ?(i ? j ) / 2?或k ? ?(i ?j ) / 2? 同时,令 s[i,j]=k,记录使用前i-1 个邮局的村庄数,便于在算出最小距离和之后构造 最优建立方案。 上述算法中 w[i,j]可通过 O(n)时间的预处理,在 O(1)的时间内算出,所以,该算法 的状态总数为 O(n*p),每个状态转移的状态数为 O(n),每次状态转移的时间为 O(1),该 算法总的时间复杂度为 O(p*n2)。 [算法优化] 本题的状态转移方程与①式十分相似,因此我们猜想其决策是否也满足单调性,即 s[i-1,j]≤s[i,j]≤s[i,j+1] 首先,我们来证明函数 w 满足四边形不等式,即:
l ?i

w[i, j ] ? ? | d [l ] ? d [k ] |

j

w[i, j ] ? w[i' , j ' ] ? w[i' , j ] ? w[i, j ' ] , i ? i' ? j ? j ' 设 y ? ?(i'? j ) / 2? , z ? ?(i ? j ' ) / 2? ,下面分为两种情形,z≤y 或 z>y,下面仅讨论
z≤y,z>y 的情况是类似的。 由 i≤z≤y≤j 有:

w[i, j ] ? w[i ' , j ' ] ? ? | d [l ] ? d [ z ] | ? ? | d [l ] ? d [ y ] |
l ?i l ?i '

j

j'

? ? | d [l ] ? d [ z ] | ? ? | d [l ] ? d [ y ] | ?
l ?i j' l ?i ' j

j

j'

l ? j ?1

? | d [l ] ? d [ z ] | ? ? | d [l ] ? d[ y] |
l ? j ?1

j'

j'

? ? | d [l ] ? d [ z ] | ? ? | d [l ] ? d [ y ] |
l ?i l ?i '

? w[i ' , j ] ? w[i, j ' ]
接着,我们用数学归纳法证明函数 m 也满足四边形不等式。对四边形不等式中“长度” l=j’-i 进行归纳: 当i=i’或 j=j’时,不等式显然成立。由此可知,当 l≤1 时,函数 m 满足四边形不等式。 下面分两种情形进行归纳证明: 情形 1:i<i’=j<j’,即 m[i,j]+m[j,j’] ≤m[i,j’],设 k=max{p | m[i,j’]=m[i,p-1]+m[p,j’]+w[i,j’] },再分两种情形 k≤j 或 k>j。 下面只讨论k≤j,k>j 的情况是类似的。

m[i, j ] ? m[ j, j ' ] ? m[i ? 1, k ] ? w[k ? 1, j ] ? m[ j ? 1, j ? 1] ? w[ j,j ' ] ? m[i ? 1, k ] ? w[k ? 1, j ' ] ? m[i, j ' ]
情形 2:i<i’<j<j’ 设 y=max{p | m[i’,j]=m[i’-1,p]+w[p+1,j] } z=max{p |m[i,j’]=m[i-1,p]+w[p+1,j’] } 仍需再分两种情形讨论,即 z≤y 或 z>y。 情形 2.1,当z≤y<j<j’时:

m[i, j ] ? m[i' , j ' ] ? m[i'?1, y] ? w[ y ? 1, j ' ] ? m[i ? 1, z ] ? w[ z ?1, j ] ? m[i'?1, y] ? m[i ? 1, z ] ? w[ y ? 1, j ] ? w[ z ? 1, j ' ] ? m[i' , j] ? m[i, j ' ]
情形 2.2,当i-1<i’-1≤y<z<j’时:


m[i, j ] ? m[i' , j ' ] ? m[i ? 1, y ] ? w[ y ? 1, j ] ? m[i'?1, z ] ? w[ z ?1, j ' ] ? m[i ? 1, z ] ? m[i'?1, y ] ? w[ y ? 1, j ] ? w[ z ? 1, j ' ]; ? m[i,j ' ] ? m[i' , j ]
最后,我们证明决策 s[i,j]满足单调性。 为讨论方便,令mk[i,j]=m[i-1,k]+w[k+1,j]; 我们先来证明 s[i-1,j]≤s[i,j],只要证明对于所有 i≤k<k’<j 且 mk’[i-1,j]≤mk[i-1,j],有:mk’[i,j]≤mk[i,j]。 类似地,我们可以证明一个更强的不等式mk[i-1,j]-mk’[i-1,j]≤mk[i,j]-mk’[i,j] 也就是:mk[i-1,j]+mk’[i,j]≤mk[i,j]+mk’[i-1,j] 利用递推定义式展开整理的:m[i-2,k]+m[i-1,k’]≤m[i-1,k]+m[i-2,k’],这就是 i-2<i-1<k<k’时 m 的四边形不等式。 我们再来证明 s[i,j]≤s[i,j+1],与上文类似,设 k<k’<j,则我们只需证明一个更强的不等式: mk[i,j]-mk’[i,j]≤mk[i,j+1]-mk’[i,j+1] 也就是: mk[i,j]+mk’[i,j+1]≤mk[i,j+1]+mk’[i,j] 利用递推定义式展开整理的:w[k+1,j]+w[k’+1,j+1]≤w[k+1,j+1]+w[k’+1,j], 这就是 k+1<k’+1<j<j+1 时 w 的四边形不等式。综上所述,该问题的决策 s[i,j]具有单调性,于是优化后的状态转移方程为:m[1,j]=w[1,j]

m[i, j ] ?

s[ i ?1, j ]? k ? s[ i , j ?1]

min

{m[i ? 1, k ] ? w[k ? 1, j ]}

i≤j

s[i,j]=k 同上文所述,优化后的算法时间复杂度为 O(n*p)。

-----IOI 2000 邮局问题f[i,j]:=min(f[I,j],f[k,j-1]+d[k+1,i]);

线型动态规划 4 Vijos 1198 最佳课题选择

-----Vijos 1198 最佳课题选择 if j-k>=0 thenMin(f[i,j],f[i-1,j-k]+time(i,k));

线型动态规划 5 APIO2007 数据备份

-----APIO2007 数据备份 状态压缩+剪掉每个阶段 j 前 j*2 个状态和 j*2+200 后的状态贪心动态规划 f:=min(g[i-2]+s,f[i-1]);


四 剖分问题
剖分问题 1 石子合并

-----石子合并 在一个园形操场的四周摆放 N 堆石子(N≤100),现要将石子有次序地合并成一堆。规定 每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该 次合并的得分。 编一程序,由文件读入堆数 N 及每堆的石子数(≤20)。 F[i,j]:=min(f[i,k]+f[k+1,j]+sum[i,j]);一.试 题 在一个园形操场的四周摆放 N 堆石子(N≤100),现要将石子有次序地合并成一堆。规定 每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该 次合并的得分。 编一程序,由文件读入堆数N 及每堆的石子数(≤20), ①选择一种合并石子的方案,使得做 N-1 次合并,得分的总和最小; ②选择一种合并石子的方案,使得做 N-1 次合并,得分的总和最大。 例如,所示的4堆石子,每堆石子数(从最上面的一堆数起,顺时针数)依 次为4594。 则3次合并得分总和最小的方案:8+13+22=43 得分最大的方案为:14+18+22=54 输入数据: 文件名由键盘输入,该文件内容为:第一行为石子堆数 N; 第二行为每堆的石子数,每两个数之间用一个空格符分隔。 输出数据: 输出文件名为 output.txt 从第 1 至第N 行为得分最小的合并方案。第 N+1 行是空行。从第 N+2 行到第 2N+1 行是得 分最大合并方案。 每种合并方案用 N 行表示,其中第 i 行(1≤i≤N)表示第 i 次合并前各堆的石子数(依 顺时针次序输出,哪一堆先输出均可)。 要求将待合并的两堆石子数以 相应的负数表示,以便标识。 输入输出范例: 输入文件内容: 4 4 59 4


输出文件内容: -4 5 9 -4 -8-5 9 -13 -9 22 4 -5 -9 4 4 -14 -4 -4-18 22 二.算法分析 竞赛中多数选手都不约而同地采用了尽可能逼近目标的贪心法来逐次合并:从最上面 的一堆开始,沿顺时针方向排成一个序列。 第一次选得分最小(最大) 的相邻两堆合并, 形成新的一堆;接下来,在N-1 堆中选得分最小(最大)的相邻两堆合 并??,依次类推,直至所有石子经 N-1 次合并后形成一堆。 例如有6堆石子,每堆石子数(从最上面一堆数起,顺时针数)依次为346 5 4 2 要求选择一种合并石子的方案,使得做5次合并,得分的总和最小。 按照贪心法,合并的过程如下: 每次合并得分 第一次合并 3 4 6 5 4 2 ->5 第二次合并 5 4 6 5 4 ->9 第三次合并 96 5 4 ->9 第四次合并 9 6 9 ->15第五次合并 15 9 ->24 24 总得分=5+9+9+15+24=62但是当我们仔细琢磨后,可得出另一个合并石子的方案: 每次合并得分 第一次合并 3 4 6 5 4 2 ->7第二次合并 7 6 5 4 2 ->13 第三次合并 13 5 4 2->6 第四次合并 13 5 6 ->11 第五次合并 13 11 ->24 24 总得分=7+6+11+13+24=61 显然,后者比贪心法得出的合并方案更优。题目中的示例故意造成一个 贪心法解题的


假像,诱使读者进入“陷阱”。为了帮助读者从这个“陷阱”里走出来, 我们先来明确一个问题: 1.最佳合并过程符合最佳原理 使用贪心法至所以可能出错,是因为每一次选择得分最小(最大)的相邻两堆合并,不一定保证余下的 合并过程能导致最优解。聪明的读者马上会想到一种理想的假设:如果 N-1 次 合并的全局最优解包含了每一次合并的子问题的最优解,那么经这样的 N-1 次 合并后的得分总和必然是最优的。 例如上例中第五次合并石子数分别为13和11的相邻两堆。这两堆石头分别由最初的第1,2,3堆(石头数分别为3,4,6)和 第4,5,6堆(石头数分别为5,4,2)经4次合并后形成的。于是问题又 归结为如何使得这两个子序列的 N-2 次合并的得分总和最优。 为了实现这一目标,我们将第1个序列又一分为二:第1、2堆构成子序列1, 第3堆为子序列2。第一次合并子序列1中的两堆,得分7; 第二次再将之与子序列2的一堆合并,得分13。显然对于第1个子序列 来说,这样的合并方案是最优的。同样,我们将第2个子序列也一分为二;第4堆为子序列 1,第5,6堆构成子序列2。第三次合并子序列2中的2堆,得 分6;第四次再将之与子序列1中的一堆合并,得分13。显然对于第二个子序 列来说,这样的合并方案也是最优的。 由此得出一个结论──6堆石子经 过这样的5次合并后,得分的总和最小。我们把每一次合并划分为阶段,当前阶段中计算出的得分和作为状态, 如何在前一次合并的基础上定义一个能使目前得分总和最大的合并方案 作为一次决策。很显然,某阶段的状态给定后,则以后各阶段的决策不受这阶段以前各段状态的影响。 这种无后效性的性质符最佳原理,因此可以用动态规划的算法求解。 2.动态规划的方向和初值的设定 采用动态规划求解的关键是确定所有石子堆子序列的最佳合并方案。这 些石子堆子序列包括: {第1堆、第2堆}、{第2堆、第3堆}、??、{第N 堆、第1堆}; {第1堆、第2堆、第3堆}、 {第2堆、第3堆、第4堆}、??、 {第 N 堆、第1堆、第2堆};?? {第1堆、??、第 N 堆}{第1堆、??、第 N 堆、第1堆}??{第 N 堆、第1堆、??、第 N-1堆} 为了便于运算,我们用〔i,j〕表示一个从第 i 堆数起,顺时针数 j 堆时 的子序列{第 i 堆、第 i+1堆、??、第(i+j-1)mod n 堆} 它的最佳合并方案包括两个信息: ①在该子序列的各堆石子合并成一堆的过程中,各次合并得分的总和; ②形成最佳得分和的子序列1和子序列2。由于两个子序列是相邻的, 因 此只需记住子序列1的堆数; 设


f〔i,j〕──将子序列〔i,j〕中的 j 堆石子合并成一堆的最佳得分和; c〔i,j〕──将〔i,j〕一分为二,其中子序列1的堆数;(1≤i≤N,1≤j≤N) 显然,对每一堆石子来说,它的 f〔i,1〕=0 c〔i,1〕=0 (1≤i≤N) 对于子序列〔i,j〕来说,若求最小得分总和,f〔i,j〕的初始值为∞; 若求最大得分总和,f〔i,j〕的初始值为0。(1≤i≤N,2≤j≤N)。 动态规划的方向是顺推(即从上而下)。先考虑含二堆石子的 N 个子序列(各子序列分别从第1堆、第2堆、??、第 N 堆数起,顺时针数2堆)的合并方案 f〔1,2〕,f〔2,2〕,??,f〔N,2〕 c〔1,2〕,c〔2,2〕,??,c〔N,2〕 然后考虑含三堆石子的N个子序列(各子序列分别从第1堆、第2 堆、??、第N堆数起,顺时针数3堆)的合并方案 f〔1,3〕,f〔2,3〕,??,f〔N,3〕 c〔1,3〕,c〔2,3〕,??,c〔N,3〕 ?? 依次类推, 直至考虑了含 N 堆石子的 N 个子序列 (各子序列分别从第1堆、 第2堆、 ??、第 N 堆数起,顺时针数 N 堆)的合并方案 f〔1,N〕,f〔2,N〕,??,f〔N,N〕 c〔1,N〕,c〔2,N〕,??,c〔N,N〕 最后,在子序列〔1,N〕,〔2,N〕,??,〔N,N〕中,选择得分总和(f 值)最小(或最大)的一个子序列〔i,N〕(1≤i≤N),由此出发倒推 合并过程。 3.动态规划方程和倒推合并过程对子序列〔i,j〕最后一次合并,其得分为第 i 堆数起,顺时针数 j 堆的 石子总数 t。被合并的两堆石子是由子序列〔i,k〕和〔(i+k-1)mod n+1,j-k〕(1≤k≤j-1)经有限次合并形成的。为了求出最佳合并方案中的 k 值,我们定义一个动态规划方程: 当求最大得分总和时 f〔i,j〕=max{f〔i,k〕+f〔x,j-k〕+t} 1≤k≤j-1 c〔i,j〕=k│ f〔i,j〕=f〔i,k〕+f〔x,j-k〕+t (2≤j≤n,1≤i≤n) 当求最小得分总和时 f〔i,j〕=min{f〔i,k〕+f〔x,j-k〕+t} 1≤k≤j-1 c〔i,j〕=k│ f〔i,j〕=f〔i,k〕+f〔x,j-k〕+t (2≤j≤n,1≤i≤n) 其中 x=(i+k-1)modn+1,即第 i 堆数起,顺时针数 k+1堆的堆 序号。


例如对上面例子中的6(3 4 6 5 4 2 )堆石子,按动态规划方程顺 推最小得分和。 依次得出含二堆石子的6个子序列的合并方案 f〔1,2〕=7 f〔2,2〕=10 f〔3 ,2〕=11 c〔1,2〕=1 c〔2,2〕=1 c〔3,2〕=1 f〔4,2〕=9 f〔5,2〕=6 f〔6,2〕=5 c〔4,2〕=1 c〔5, 2〕=1c〔6,2〕=1 含三堆石子的6(3 4 6 5 4 2 )个子序列的合并方案 f〔1,3〕=20 f〔2,3〕=25f〔3,3〕=24 c〔1,3〕=2 c〔2,3〕=2 c〔3,3〕=1 f〔4,3〕=17f〔5,3〕=14 f〔6,3〕=14 c〔4,3〕=1 c〔5,3〕=1 c〔6,3〕=2 含四堆石子的6(3 4 6 5 4 2 )个子序列的合并方案 f〔1,4〕=36 f〔2,4〕=38f〔3,4〕=34 c〔1,4〕=2 c〔2,4〕=2 c〔3,4〕=1 f〔4,4〕=28f〔5,4〕=26 f〔6,4〕=29 c〔4,4〕=1 c〔5,4〕=2 c〔6,4〕=3 含五堆石子的6(3 4 6 5 4 2 )个子序列的合并方案 f〔1,5〕=51 f〔2,5〕=48f〔3,5〕=45 c〔1,5〕=3 c〔2,5〕=2 c〔3,5〕=2 f〔4,5〕=41f〔5,5〕=43 f〔6,5〕=45 c〔4,5〕=2 c〔5,5〕=3 c〔6,5〕=3 含六堆石子的6(3 4 6 5 4 2 )个子序列的合并方案 f〔1,6〕=61 f〔2,6〕=62f〔3,6〕=61 c〔1,6〕=3 c〔2,6〕=2 c〔3,6〕=2 f〔4,6〕=61f〔5,6〕=61 f〔6,6〕=62 c〔4,6〕=3 c〔5,6〕=4 c〔6,6〕=3 f〔1,6〕是 f〔1,6〕,f〔2,6〕,??f〔6,6〕中的最小 值,表明最小得分和是由序列〔1,6〕经5次合并得出的。我们从这个序列出发, 按下述方法倒推合并过程: 由 c〔1,6〕=3可知,第5次合并的两堆石子分别由子序列〔1,3〕 和子序列〔4,3〕经4次合并后得出。其中 c〔1,3〕=2可知由子序列〔1, 3〕 合并成的一堆石子是由子序列〔1,2〕和第三堆合并而来的。而 c〔1, 2〕=1,以表明了子序列〔1,2〕的合并方案是第1堆合并第2堆。 由此倒推回去,得出第1,第2次合并的方案,每次合并得分第一次合并 3 4 6?? ->7 第二次合并 7 6?? ->1313?? 子序列〔1,3〕经2次合并后合并成1堆, 2次合并的得分和=7+ 13=20。


c〔4,3〕=1,可知由子序列〔4,3〕合并成的一堆石子是由第4 堆和子序列〔5, 2〕合并而来的。而 c〔5,2〕=1,又表明了子序列〔5,2〕的合并方案是第5堆合并第6堆。由此倒推回去,得出第3、第4次合并的方案 每次合并得分: 第三次合并 ??54 2 ->6 第四次合并 ??5 6->11 ??11 子序列〔4,3〕经2次合并后合并成1堆,2次合并的得分和=6+1 1=17。第五次合并是将最后两堆合并成1堆,该次合并的得分为24。 显然,上述5次合并的得分总和为最小 20+17+24=61 上述倒推过程,可由一个 print(〔子序列〕)的递归算法描述 procedure print (〔i,j〕) begin if j〈〉1then {继续倒推合并过程 begin print(〔i,c〔i,j〕〕;{倒推子序列1的合并过程} print(〔i+c〔i,j〕-1〕mod n+1,j-c〔i,j〕) {倒推子序列2的合并过程} for K:=1 to N do{输出当前被合并的两堆石子} if (第 K 堆石子未从圈内去除) then begin if(K=i)or(K=X)then 置第 K 堆石子待合并标志 else 第 K堆石子未被合并; end;{then} 第 i 堆石子数←第 i 堆石子数+第 X 堆石子数; 将第 X 堆石子从圈内去除; end;{then} end;{print} 例如,调用 print(〔1,6〕)后的结果如下: print(〔1,6〕)⑤ ┌──────┴──────┐ print(〔1,3〕)② print(〔4, 3〕)④ ┌─────┴─────┐ ┌─────┴─────┐ print( 〔1, )① print( 2〕 〔3, ) 1〕 print( 〔4, ) print( 1〕 〔5, 2〕)③ ┌──────┴──────┐ ┌── ────┴──────┐ print(〔1,1〕) print(〔2,1〕) print


(〔5,1〕) print(〔6,1〕) (图 6.2-5) 其中回溯至 ① 显示 3 46 5 4 ② 显示 7 65 4 2 ③ 显示 13 54 2 ④ 显示 135 6 ⑤ 显示 13 11 注:调用print 过程后,应显示6堆石子的总数作为第5次合并的得分。 Program Stones; TypeNode = Record{当前序列的合并方案} c : Longint;{得分和} d : Byte{子序列 1 的堆数}End; SumType = Array [1..100,1..100] of Longint; {sumtype[i,j]-子序列[i,j]的石子总数} Var List : Array [1..100,1..100]of Node; {list[i,j]-子序列[i,j]的合并方案} Date, Dt : Array [1..100] of Integer; {Date[i]-第 i 堆石子数,Dt-暂存 Date}Sum : ^SumType;{sum^[i,j]-指向子序列[i,j]的石子总数的指针} F : Text;{文件变量} Fn : String;{文件名串} N, i, j : Integer;{N-石子堆数,i,j-循环变量} Procedure Print(i, j : Byte);{递归打印子序列[i,j]的合并过程} Var k, x : Shortint;{k-循环变量;x-子序列 2 中首堆石子的序号} Begin If j <> 1 ThenBegin{继续倒推合并过程} Print(i, List[i,j].d);{倒推子序列 1 的合并过程} x := (i + List[i, j].d - 1) Mod N+ 1;{求子序列 2 中首堆石子的 序号} Print(x,j - List[i, j].d);{倒推子序列 2 的合并过程} For k := 1 to N Do{输出当前合并第 i 堆,第 x 堆石子的方案} IfDate[k] > 0 Then Begin If (i= k)or(x=k)Then Write(F, - Date[k], ' ') ElseWrite(F, Date[k], ' ') End; { Then } Writeln(F);{输出换行符}


Date[i] := Date[x] := End { Then End; { Print

Date[i] + Date[x];{原第 i 堆和第 x 堆合并成第i堆} - Date[x]{将原第 x 堆从圈内去除} } }

Procedure Main(s : Shortint); Var i, j, k : Integer; t, x : Longint; Begin Fori := 1 to N Do Begin{仅含一堆石子的序列不存在合并} List[i, 1].c := 0;List[i, 1].d := 0 End; {For} For j := 2 to N Do{顺推含 2 堆,含 3 堆??含 N 堆石子的各子序列的 合并方案} For i := 1 to N Do Begin{当前考虑从第 i 堆数起,顺时针数 j 堆的子序列} If s = 1 Then List[i, j].c := Maxlongint{合并[i,j]子序列的 得分和初始化} Else List[i, j].c := 0; t:= Sum^[i, j];{最后一次合并的得分为[i,j]子序列的石子总数} For k := 1 to j - 1 Do Begin{子序列 1 的石子堆数依次考虑 1 堆??j-1 堆} x := (i+ k - 1) Mod N + 1;{求子序列 2 首堆序号}If (s=1) And (List[i,k].c + List[x,j-k].c+t < List[i, j].c) Or (s=2) And(List[i,k].c + List[x,j-k].c+t > List[i, j].c) { 若该合并方案为目前最佳,则记下} Then Begin List[i, j].c := List[i,k].c + List[x, j - k].c + t; List[i, j].d := k End { Then } End { For } End; {For } {在子序列[1,N],[2,N],??,[N, N]中选择得分总和最小(或最大)的一 个子序列} k :=1; x := List[1, N].c; For i := 2 to N Do If (s = 1) And (List[i, N].c < x)Or (s = 2) And (List[i, N].c > x) Then Begin k := i; x := List[i, N].c End;{ Then } Print(k, N);{由此出发,倒推合并过程} Writeln(F, Sum^[1, N]);{输出最后一次将石子合并成一堆的石子总数}


Writeln(F); Writeln(list[k, N].c) End; { Main } Begin Write('File name = ');{输入文件名串} Readln(Fn); Assign(F, Fn);{该文件名串与文件变量连接}Reset(F);{文件读准备} Readln(F, N);{读入石子堆数} For i := 1 to N Do Read(F, Date[i]);{读入每堆石子数} New(Sum);{求每一个子序列的石子数 sum} For i := 1 to NDo Sum^[i, 1] := Date[i]; For j := 2 to N Do For i := 1 to N Do Sum^[i, j] :=Date[i] + Sum^[i Mod N + 1, j - 1]; Dt := Date;{暂存合并前的各堆石子,结构相同的变量可相互赋值} Close(F);{关闭输入文件} Assign(F, 'OUTPUT.TXT');{文件变量与输出文件名串连接}Rewrite(F);{文件写准备} Main(1);{求得分和最小的合并方案} Date := Dt;{恢复合并前的各堆石子} Main(2);{求得分和最大的合并方案} Close(F){关闭输出文件} End.

#include <iostream> #include <set> #include <vector> #defineMAX 6 using namespace std; int dis[MAX][MAX]={ 0, 10, 20, 30, 40, 50, 12, 0,18, 30, 25, 21, 23, 19, 0, 5, 10, 15, 34, 32, 4, 0, 8, 16, 45, 27, 11,10, 0,18, 56, 22, 16,20, 12, 0


}; typedef struct { int curcity;//当前所在的城市vector<int> unvisited;//当前未访问的城市 set<int>type;//由于 set 自动排序,相同状态的 vector可能不同,但 set 必然相同 int distance;//从当前城市到终点回到起点的距离 }status; /*测试用*/ void printVec(vector<status> vec) { vector<status>::iterator iter;vector<int>::iterator it; for(iter=vec.begin();iter!=vec.end();iter++) {cout<<(*iter).curcity<<" <";for(it=(*iter).unvisited.begin();it!=(*iter).unvisited.end();it++) {cout<<*it<<" "; } cout<<">"<<"distance:"<<(*iter).distance<<endl; } } //看看当前状态的城市中是否包括城市 i bool contain(int i, status &sta) {vector<int>::iterator iter; if(i==sta.curcity) return true; else {for(iter=sta.unvisited.begin();iter!=sta.unvisited.end();iter++) if(i==*iter)return true; } return false; } /*合并相同状态*/vector<status> combine(vector<status> vec) { vector<status>new_vec;


vector<status>::iterator iter; status temp; while(vec.size()>0) {iter=vec.begin(); temp=*iter; vec.erase(iter); for(;iter!=vec.end();iter++) {if((temp.curcity==(*iter).curcity)&&(temp.type==(*iter).type)) {if((*iter).distance<temp.distance) temp=*iter; iter=vec.erase(iter); iter--;} } new_vec.push_back(temp); } return new_vec; } int main() {vector<status> pre_vector; vector<status> cur_vector; //从后往前推,初始化 for(int i=1;i<MAX;i++) { status sta; sta.curcity=i;sta.distance=dis[i][0]; cur_vector.push_back(sta); } //依次递推,递推 MAX-2 次 for(int j=0;j<MAX-2;j++){pre_vector=cur_vector; cur_vector.clear(); for(int i=1;i<MAX;i++) {vector<status>::iterator iter;for(iter=pre_vector.begin();iter!=pre_vector.end();iter++)


{ status temp=*iter; if(contain(i,temp)==false)//为确保状态中没有重复路径 { status new_stat=temp; vector<int>::iteratorint_iter=new_stat.unvisited.begin();new_stat.unvisited.insert(int_iter,new_stat.curcity);//加入 vector new_stat.type.insert(new_stat.curcity);//加入 set new_stat.distance+=dis[i][new_stat.curcity];//计算距离 new_stat.curcity=i; cur_vector.push_back(new_stat); } } } //记录相同状态最短路径,并合并相同状态 cur_vector=combine(cur_vector); }//end for

//printVec(cur_vector);

//递推完毕后,最后一步,计算起点到每个状态的距离,找到最短路径vector<status>::iterator iter=cur_vector.begin(); status shortest=*iter;int min_dis=shortest.distance+dis[0][shortest.curcity]; iter++;for(;iter!=cur_vector.end();iter++) { int temp_dis=dis[0][(*iter).curcity]+(*iter).distance;if(temp_dis<min_dis) { min_dis=temp_dis; shortest=*iter; } } //打印结果 vector<int>::iterator iter_city; cout<<"minimumdistance is "<<min_dis<<endl; cout<<"the shortestpath is "<<"1 "<<shortest.curcity+1;for(iter_city=shortest.unvisited.begin();iter_city!=shortest.unvisited.end();iter_city++)cout<<" "<<*iter_city+1; cout<<"1"<<endl; return 0;


}

剖分问题 2 多边形剖分

-----多边形剖分 有 N 个顶点(从 1 到 N 编号)的凸多边形,每个顶点的权均已知。问如何把这 个凸多边形划分成 N-2 个互不相交的三角形,使得这些三角形顶点的权的乘积之 和最小?

Input
输入数据: 第一行 顶点数 N(N<50)。第二行 N 个顶点(从 1 到 N)的权值,权值为小于 32768 的整数。

Output
输出数据: 第一行为各三角形顶点的权的乘积之和最小值。

Sample Input
division.in 5 121 122 123

245

231

Sample Output
division.out 12214884

Source

分析:


由一个点射出的一条线可以把多边形分成左右两部分,那么就转化成了相同类 型的子问题。 该多边形的权值乘积之和最小值就等于左边部分的最小值与右边部分的最小值相加。
P.S.本来以为还得用高精度,结果刚才看了看原来写的程序发现 long long 完全可以搞定。

F[I,j]:=min(f[i,k]+f[k,j]+a[k]*a[j]*a[i]); i<k<j;

源程序:
#include<iostream> #define MAX 50 using namespace std; int n,w[MAX]; longlong f[MAX][MAX]; void init() { int i; cin>>n; for(i=1;i<=n;i++)cin>>w[i]; memset(f,0,sizeof(f)); } void solve() { int l,i,j,k; long longtemp,t1; for(l=2;l<=n;l++) for(i=1;i<n;i++) { j=i+l-1;for(k=i+1;k<j;k++) { t1=w[i]; t1*=w[j]; t1*=w[k]; temp=f[i][k];temp+=f[k][j]; temp+=t1;


if(f[i][j]==0||f[i][j]>temp) f[i][j]=temp; } } } void print() {cout<<f[1][n]; } int main() { init(); solve(); print(); return 0; }

剖分问题 3 乘积最大

------乘积最大 今年是国际数学联盟确定的“2000——世界数学年”,又恰逢我国著名数学 家华罗庚先生诞辰 90 周年。在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友 XZ 也有幸得以参加。活动中,主持 人给所有参加活动的选手出了这样一道题目:设有一个长度为 N 的数字串,要求选手使用 K 个乘号将它分成 K+1 个部分, 找出一种分法,使得这 K+1 个部分的乘积能够为最大。同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子: 有一个数字串:312, 当 N=3,K=1 时会有以下两种分法:1) 2) 3*12=36 31*2=62


这时,符合题目要求的结果是:31*2=62 现在,请你帮助你的好朋友 XZ 设计一个程序,求得正确的答案。
【分析】设 f[i,j]表示前 j 个数划分成 i 段的最大乘积,w[i,j]表示第i 个数字到第 j 个数字的 数f[i,j]=max(f[i-1,j-1]*w[j,i]) (0<=k<=n)(1<j<=i<=n)

f[i,j]:=max(f[k,j-1]*mult[k,i]);

【源程序】 Program math; Var f:array[0..31,1..40]of qword;a:string;t:qword; n,k,i,j,h:integer; function make(x,y:integer):qword; vars:qword;i:integer; begin s:=0; for i:=x to y do s:=s*10+ord(a[i])-ord('0');exit(s); end; Begin fillchar(f,sizeof(f),0); readln(n,k);readln(a); for i:=1 ton do f[0,i]:=make(1,i); for h:=1 to k do for i:=h to n do for j:=1 to i dobegin t:=f[h-1,j-1]*make(j,i); if t>f[h,i] then f[h,i]:=t; end; writeln(f[k,n]);End.


剖分问题 4 多边形-讨论的动态规划

-----多边形-讨论的动态规划
多角形是一个单人玩的游戏,开始时有一个 N 个顶点的多边形。如图,这里 N=4。每个顶点有一个整数标记,每条边上有一个“+”号或“*”号。边从 1 编号到 N。第一步,一条边被拿走;随后各步包括如下: 选择一条边 E 和连接着 E 的两个顶点 V1 和 V2; 得到一个新的顶点,标记为 V1 与 V2 通过边 E 上的运算符运算的结果。最后,游戏中没有边,游戏的得分为仅剩余的一个顶点的值。

我们先枚举第一次删掉的边,然后再对每种状态进行动态规划求最大值 。 用 f(i,j)表示从点 i 到点 j 进行删边操作所能得到的最大值,num(i)表示第 i 个顶点上的数,若为加法,那么:

f (i, j ) ? max ? ( f (k , i) ? f ( j ? k , i ? k )) 2 k ?1 f (i, i) ? num(i)
最后,我们允许顶点上出现负数。以前的方程还适不适 用呢?
-10 * 3 图六 + 2 * -5

i ?1

- 7 1 + 5

+

4 * 3

* 4

2

这个例子的最优解应该是(3+2)*(-10)*(-5)=250,然而如果沿用以 前的方程,得出的解将是((-10)*3+2)*(-5)=125。为什么? 我们发现,两个负数的积为正数;这两个负数越小,它们的积越大。我们从前 的方程,只是尽量使得局部解最大,而从来没有想过负数的积为正数这个问题。对于加法,两个最优相加肯定最优,而对于乘法 求最大值: 正数×正数,如果两个都是最大值,则结果最大 正数×负数,正数最小,负数最大,则结果最大 负数×负数,如果两个都是最小值,则结果最大求最小值: 正数×正数,如果两个都是最小值,则结果最小


正数×负数,正数最大,负数最小,则结果最小 负数×负数,如果两个都是最大值,则结果最小我们引入函数 fmin 和 fmax 来解决这个问题。fmax(i,j) 表示从点 i 开始, 到但 j 为止进行删边操作所能得到的最大值,fmin(i,j) 表示最小值。 当 OP=‘+’ Fmax(i,j)=max{fmax(i,k)+fmax(k+1,j)}Fmin(i,j)=min{fmin(i,k)+fmin(k+1,j)} 当 OP=‘*’

?fmax(i, k ) * f max(k ? 1, j ) ?fmax(i, k ) * f min(k ? 1, j ) ? f max(i, j )? ? ?fmin(i, k ) * f max(k ? 1, j ) ?fmin(i, k ) * f min(k ? 1, j ) ?

?fmin(i, k ) * f min(k ? 1, j ) ?fmax(i, k ) * f min(k ? 1, j ) ? f min(i, j )? ? ?fmin(i, k ) * f max(k ? 1, j ) ?fmax(i, k ) * f max(k ? 1, j ) ?
初始值 Fmax(i,i)=num(i) Fmin(i,i)=num(i)1<=i<=k=<j=n 空间复杂度:O(n2) 时间复杂度:先要枚举每一条边为 O(n),然后动态规划为 O(n3 ),因此总为 O(n4)。F[i,j]:=max{正正 f[I,k]*f[k+1,j]; 负负 g[I,k]*f[k+1,j]; 正负 g[I,k]*f[k+1,j]; 负正 f[I,k]*g[k+1,j];} g 为 min

剖分问题 5 最大奖励

-----最大奖励


f:=max(f,f[j]+(sum[j]-sum)*i-t

剖分问题 6 小 H 的小屋

-----小 H 的小屋F[l,m,n]:=f[l-x,m-1,n-k]+S(x,k);

贪心的动态规划
贪心的动态规划 1 快餐问题

-----快餐问题
一、问题描述 Peter 最近在 R 市开了一家快餐店,为了招揽顾客,该快餐店准备推出一种套餐,该套餐由 A 个汉堡,B 个薯条和 C 个饮料组成。价格便宜。为了提高产量,Peter 从 著名的麦当劳公司引进了 N 条生产线。所有的生产线都可以生产汉堡,薯条和饮 料,由于每条生产线每天所能提供的生产时间是有限的、不同的,而汉堡,薯条和饮 料的单位生产时间又不同。这使得 Peter 很为难,不知道如何安排生产才能使一 天中生产的套餐产量最大。请你编一程序,计算一天中套餐的最大生产量。为简单 起见,假设汉堡、薯条和饮料的日产量不超过 100 个。 输入: 输入文件共四行。 第一行为三个不超过 100 的正整数 A、 C 中间以一个空格分开。 B、 第三行为 3 个不超过 100 的正整数 p1,p2,p3 分别为汉堡, 薯条和饮料的单位生产耗 时。中间以一个空格分开。 第三行为 N(0<=0<=10),第四行为 N 个不超过 10000 的 正整数,分别为各条生产流水线每天提供的生产时间,中间以一个空格分开。

F[i,j,k]:=max{f[i-1,j',k']+(T[i]-(j-j')*p1-(k-k')*p2) div p3} 输出: 每天套餐的最大产量。 分析 本题是一个非常典型的资源分配问题。 由于每条生产线的生产是相互独 立, 不互相影响的, 所以此题可以以生产线为阶段用动态规划求解。状态表 示: 用 p[i, j, k] 表示前 i 条生产线生产 j 个汉堡, k 个薯条的情况下最多 可生产饮料的个数。 用 r[i, j, k] 表示第 i 条生产线生产 j 个汉堡, k 个薯 条的情况下最多可生产饮料的个数。 态转移方程 : p[i, j, k] = Max{p[i-1, j1, k1] +r[i, j-j1, k-k1] } (0<=j1<=j<=100, 0<=k1<=k<=100, 且(j-j1)*p1+(k-k1) *p2<=T[i] ---第 i 条生产 线的生产时间 ) r[i, j-j1, k-k1] =(T[i] -(j-j1) *p1+(k-k1) *p2) div


p3 ; 此算法的时间复杂度为 O(N* 1 00 4 ), 优化 在本题中, 可以在动态 规划方法中加入了贪心算法思想:即首先计算出每天生产套数的上限值(由 A, B,C 计算, 即 mi n{1 00 di v A, 1 00 di v B, 1 00 di v c} ) , 接着, 用贪心法计算出这 N 条流水线可以生产的套数,并与上限比较, 大于 则输出上限值并退出, 否则再调用动态规划; 同时, 在运行动态规划的过程 中, 也可以每完成一阶段工作便与上限值进行比较, 这样以来, 便可望在动态规划完成前提前结束程序。 其算法设计为: S1 : 读入数据。 S2:贪心 求上限并计算出一可行解, 判断是否需进行下一步。 S3: 动态规划求解。 S4: 输出。 其他优化方法 1. 存储结构: 由于每一阶段状态只与上一阶段状态有关,所以我们可以只用两个 100*100 的数组滚动实现。但考虑到滚动是有大量的 赋值, 可以改进为动态数组, 每次交换时只需交换指针即可,这样比原来数 组间赋值要快。 2. 减少循环次数: 在计算每一阶段状态过程中无疑要用到 4 重循环, 我们可以这样修改每一重循环的起始值和终数, 其中 q1, q2 为 A, B 上限值。 原起始值 改进后的起始值 for i : =1 to n do for i : =1 to n do for j : =0 to tot[i ] di v p1do for j : =0 to mi n(q1 , tot[i ] di v p1 ) do for k: =0 to (tot[i ] -p1 *j )di v p2 do for k: =0 to mi n(q2, (tot[i ] -p1 *j ) di v p2) do for j 1 : =0 toj do for j 1 : = max(0, j -t[i ] di v p1 ) to mi n(j , tot[i -1 ] di v p1 ) dofor k1 : = 0 to k do for k1 : =max(0, k-(t[i ] -(j -j 1 ) *p1 ) di v p2) to min(k, (tot[i -1 ] -p1 *j 1 ) di v p2) do

贪心的动态规划 2(过河)
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙 很讨厌踩在这些石子上。 由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上 青蛙可能到达的点看成数轴上的一串整点:0,1,⋯⋯,L(其中 L 是桥的长度)。坐标为 0 的 点表示桥的起点,坐标为 L 的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。 一次跳跃的距离是 S到 T 之间的任意正整数(包括 S,T)。当青蛙跳到或跳过坐标为 L 的点时, 就算青蛙已经跳出了独木桥。 题目给出独木桥的长度 L,青蛙跳跃的距离范围 S,T,桥上石子的位置。你的任务是确定青蛙要 想过河,最少需要踩到的石子数。

-----过河 f[i]=min{{f(i-k)} (not stone[i]) {f(i-k)}+1}(stone[i]); +贪心压缩状态
算法分析: 看到题目首先想到的是时间复杂度为 O(L)的递推算法。但是 L 的上限为 10^9,这种算法显然是不行的。


仔细思考,可以得到下面的结论: 存在 N0,当 n>N0 时,n 可以由若干 S 到 T 之间的正整数(包括 S,T)组成。 因此,将障碍点按升序排列,当两相邻障碍点之间距离较大时,可适当缩小两障碍点之间距离,但不影响最终结果。 根据上述结论,改进递推算法。由于障碍点之间距离大大缩减,算法的复杂度是可以承受的。 特别地,当 S=T 时需要单独处理。程序: program river; const max=105; var a,a1:array[0..101] oflongint; b:array[0..100] of boolean; c,d:array[0..10000] of longint; l,s,t,m,ans,low,i,j,k,temp:longint;flag:boolean; f:text; procedure init; begin assign(f,'river9.in');reset(f);readln(f,l); readln(f,s,t,m); for i:=1 to m do read(f,a[i]); a[0]:=0;a[m+1]:=l;for i:=1 to m-1 do for j:=i+1 to m do if a[i]>a[j] then begin temp:=a[i];a[i]:=a[j];a[j]:=temp;end; close(f); end; procedure work1; begin for i:=1 to m do if a[i] mod s=0then inc(ans); end; procedure work2;


begin fillchar(b,sizeof(b),false); b[0]:=true; for i:=s to t do begin for j:=0to 100 do if b[j] then begin k:=1; while k*i+j<=100 do begin b[k*i+j]:=true;inc(k); end; end; end; for i:=1 to 100 do begin flag:=true; for j:=0 to t-1 doif not b[i+j] then begin flag:=false;break;end; if flag then begin low:=i;break; end; end; if low<t then low:=t; for i:=1 to m+1 do begina1[i]:=(a[i]-a[i-1]-low) mod low+a1[i-1]+low; end; a:=a1; for i:=1 to m dod[a[i]]:=1; l:=a[m+1]; for i:=1 to l+t-1 do c[i]:=max;


for i:=1 to l+t-1 do for j:=s to t do if (i-j>=0) and (c[i]>c[i-j]+d[i])then c[i]:=c[i-j]+d[i]; ans:=max; for i:=l to l+t-1 do if ans>c[i] thenans:=c[i]; end; begin init; if s=t then work1 else work2;assign(f,'river.out');rewrite(f); writeln(f,ans); close(f); end.

计数问题
计数问题 1 砝码称重

-----砝码称重
现有 1g、2g、3g、5g、10g、20g 的砝码各若干枚,问用这些砝码可以称出多少种不同的重量。 (设砝码的总重量不超过1000 克,且砝码只能放在天平的一端)

这一题 f[i][i1]=f[i-1][i1-n*a[i]] 表示的是用前 i 个砝码表示 i1 这个重量如果能表 示的话就记录一下,因为已经告诉了最大可得到的重量是 1000 所以 i1 的范围就 知道了,当然也可以自己算可以节省一些时间。 f[f[0]+1]=f[j]+k*w[j]; (1<=i<=n; 1<=j<=f[0];1<=k<=a[i];)
#include<stdlib.h> #include<stdio.h> int main() { intk=0,i2,i1,i,a[10][3],f[101][1010]={0},b[1010]={0}; for(i=1;i<=6;i++)scanf("%d",&a[i][1]);a[1][2]=1;a[2][2]=2;a[3][2]=3;a[4][2]=5;a[5][2]=10;a[6][2]=20;


for(i=0;i<=6;i++) f[i][0]=1; for(i1=1;i1<=6;i1++) for(i=1;i<=1000;i++){ for(i2=0;i2<=a[i1][1];i2++) { if(f[i1-1][i-i2*a[i1][2]]==1) { f[i1][i]=1;if(b[i]<1) { b[i]=1; k++; } break; } } } printf("%d",k);system("pause"); }

计数问题 2 陨石的秘密

-----陨石的秘密(排列组合中的计数问题)Ans[l1,l2,l3,D]:=f[l1+1,l2,l3,D+1]-f[l1+1,l2,l3,D];F[l1,l2,l3,D]:=Sigma(f[o,p,q,d-1]*f[l1-o,l2-p,l3-q,d]);
一、问题描述 公元 11380 年,一颗巨大的陨石坠落在南极。于是,灾难降临了,地球上出现了一系列反常的现象。当人们焦急万分的时候,一支中国科学家组成的南极考察队赶到了出事地点。 经过一番侦察,科学家们发现陨石上刻有若干行密文,每一行都包含 5 个整数: 1 1 1 1 6 0 0 6 3 57 8 0 11 3 2845 著名的科学家 SS 发现,这些密文实际上是一种复杂运算的结果。为了便于大家理解这 种运算,他定义了一种 SS 表达式: 1. SS 表达式是仅由‘{’‘}’‘[’‘]’‘ , ’组成的字符串。 , , , ,(’‘) 2. 一个空串是 SS 表达式。 3. 如果 A 是 SS 表达式,且 A 中不含字符‘{’‘}’‘[’‘]’ , , , ,则(A)是 SS 表达式。 4.如果 A 是 SS 表达式,且 A 中不含字符‘{’‘}’ , ,则[A]是 SS 表达式。 5. 如果 A 是 SS 表达式,则{A}是 SS 表达式。 6. 如果 A 和 B 都是 SS 表达式,则 AB 也是 SS 表达式。


例如

()(())[] {()[()]} {{[[(())]]}}
都是 SS 表达式。 而

()([])() [()
不是 SS 表达式。 一个 SS 表达式 E 的深度 D(E)定义如下:

?0, 如果E是空串 ? D( E ) ? ? D( A) ?1, 如果E ? ( A)或者E ? [ A]或者E ? { A}, 其中A是SS 表达式 ? max( D( A), D( B )), 如果E ? AB, 其中A,B是SS 表达式。 ?
例如(){()}[]的深度为 2。 密文中的复杂运算是这样进行的:设密文中每行前 4 个数依次为 L1,L2,L3,D,求出所有深度为 D,含有 L1 对{},L2 对[],L3 对()的 SS 串的个数,并用这个数对当前的年份 11380 求余数,这个余数就是密文 中每行的第 5 个数,我们称之为“神秘数” 。 密文中某些行的第五个数已经模糊不清, 而这些数字正是揭开陨石秘密的钥匙。现在科 学家们聘请你来计算这个神秘数。

输入文件(secret.in)

共一行,4 个整数 L1,L2,L3,D。相邻两个数之间用一个空格分隔。(0≤L1≤10,0≤L2≤10,0≤L3≤10,0≤D≤30)

输出文件(secret.out)

共一行,包含一个整数,即神秘数。

输入样例

1 1 1 2

输出样例

8 二、分析解答


这是一个典型的计数问题。 动态规划的一个重要应用就是组合计数—如鱼得水, 具有编程简单、 时空复杂度低等优点。我们自然想到:是否本题也可以用动态规划来解决呢? 条件的简化 题目对于什么是 SS 表达式做了大量的定义,一系列的条件让我们如坠雾中。为了看清 SS 表达式的本质,有必要对条件进行简化。 条件 1 描述了 SS 表达式的元素。 条件 3、4、5 实际上对于()、[]、{}的嵌套顺序做了限制,即()内不能嵌套[]、{}, []内不能潜逃{}。概括起来是两点:SS 表达式中括号要配对;{}、[]、()从外到内依次 嵌套。 状态的表示 这是动态规划过程中首先要解决的一个问题。 本题的条件看似错综复杂, 状态不易提炼 出来,实际上,题目本身已经为我们提供了一个很好的状态表示法。对于一个表达式来说,它含有的元素是“ , ”“[”“]”“{”“}” (”“), , , , ,此外,定义 了深度这一概念。最简单的一种想法是:按照题目的所求,直接把{}的对数 l1、[]的对数 l2、()的对数 l3 以及深度 d 作为状态表示的组成部分,即用(l1,l2,l3,d)这样一个四元组来确定一个状态。令 F(l1,l2,l3,d)表示这样一个状态所对应的神秘数,于是 F(L1,L2,L3,D) 对应问题答案。此外,我们令 G(l1,l2,l3,d)表示含有 l1 个{},l2 个[],l3 个(),深度不大于 d 的表达式个数。显然,F(l1,l2,l3,d)=G(l1,l2,l3,d)-G(l1,l2,l3,d-1)。于是求解 F 的问题,可以转化为求解 G 的问题。 状态转移方程的建立 设当前的状态为(l1,l2,l3,d) ,根据表达式的第一位的值,分如下三种情况: 情况一:第一位是“ ,与其配对的“) (” ”位于第 i 位。设 G1 (l1, l 2, l 3, d ) 表示这种情 况下的总数, G 2 、 G3 类似定义。

( ss1

) i ss2

()将整个表达式分成两部分(图中的 ss1 和 ss2)。根据乘法原理,我们只需对两部分分 别计数,然后乘起来即为结果。 我们设ss1 中含有 x 对{},y 对[],z 对()。因为 ss1 外层已经由一对()括起来,故其内部不可再含[]、{},因此 x=0,y=0,且 ss1 的深度不可超过 d-1,ss1 的数目为G(x,y,z,d-1)=G(0,0,z,d-1)。ss2 中含有 l1-x=l1 个{},l2-y=l2个[],l3-z-1 个(), 深度不可超过 d,ss2 的数目为 G(l1,l2,l3-z-1,d)。据此,我们写出下面这个式子:

G1 (l1, l 2, l 3, d ) ? ? G(0,0, z, d ? 1) * G(l1, l 2, l 3 ? z ? 1, d )
z ?0

l 3?1

情况一计算的复杂度为 O(n^5)。


情况二:第一位是“[” ,与其配对的“]”位于第 i 位。

[ ss1
与情况一类似可得出

] i ss2

G2 (l1, l 2, l 3, d ) ?

y ?? l 2 ?1, z ?? l 3

? G(0, y, z, d ? 1) * G(l1, l 2 ? y ? 1, l3 ? z, d )

计算复杂度为 O(n^6)。 情况三:第一位是“{” ,与其配对的“}”位于第 i 位。

{ ss1
有如下式子:

} i ss2

G3 (l1, l 2, l 3, d ) ?

x ?? l1?1 y ?? l 2, z ?? l 3

? G( x, y, z, d ? 1) * G(l1 ? x ? 1, l 2 ? y, l 3 ? z, d )

这一部复杂度为 O(n^7)。 综合上述三种情况:

G(l1, l 2, l 3, d ) ? G1 (l1, l 2, l 3, d ) ? G2 (l1, l 2, l 3, d ) ? G3 (l1, l2, l 3, d )
三、小结 本题的时间复杂度为 O(n^7),在规定时间内完全可以出解。空间上,若采用滚动数组 的方式,可将空间复杂度为 n^3,保存也不成问题。本题的难点在于动态规划时情况的划 分及处理,当需要建立复杂的状态转移方程时,我们也要保持冷静、抓住要害。四、参考程序 program Secret; const finp fout year

= 'secret.in'; = 'secret.out'; = 11380;

var f : array[0 .. 11 , 0 .. 11 , 0 .. 11 , -1 .. 31] of integer; a1 , a2 , a3, dep : integer;


procedure calc; var s : integer; d , l1 , l2 , l3 , x , y , z : integer; beginfillword(f , sizeof(f) shr 1 , 0); for d := 0 to dep do f[0,0,0,d] := 1; for d:= 1 to dep do for l1 := 0 to a1 do for l2 := 0 to a2 do for l3 := 0 to a3 doif (l1 > 0) or (l2 > 0) or (l3 > 0) then begin s := 0; for x := 0 tol1 - 1 do for y := 0 to l2 do for z := 0 to l3 do s := (s + f[x,y,z,d-1] *f[l1-x-1,l2-y,l3-z,d]) mod year; for y := 0 to l2 - 1 do for z := 0 to l3 do s:= (s + f[0,y,z,d-1] * f[l1,l2-y-1,l3-z,d]) mod year; for z := 0 to l3 - 1 do s:= (s + f[0,0,z,d-1] * f[l1,l2,l3-z-1,d]) mod year; f[l1,l2,l3,d] := s; end;end; procedure init; begin assign(input , finp); reset(input); readln(a1 , a2 ,a3 , dep); close(input); end; procedure print; var left : longint; beginassign(output , fout); rewrite(output);


left := f[a1,a2,a3,dep] - f[a1,a2,a3,dep-1]; if left < 0 then left := left +year; writeln(left); close(output); end; begin init; calc; print; end.

最大子矩阵
最大子矩阵 1 最大 01 子矩阵

-----一最大 01 子矩阵 设 h[i, j]为元素 a[i j]的高度,其定义如下:若 a[i j]为 1,则 h[i,j]=0,否则 定义为从该元素开始在同一列上向上延伸的连续的零(0)的个数.考虑面积最大 的全零(0)子矩阵的最后一行,则要求的面积最大的全零(0)子矩阵的高度必为其 最后一行所有零(0)元素中高度最小的元素的高度,否则该子阵必不是最大的,与 假设矛盾.再定义 l[i, j]为从第 i 行第 j 列开始向左延伸高度均不小于 h[i, j] 的最左一列的列号,即从该列开始至第 j 列的每个元素的高度均不小于 h[i,j], 同样定义 r[i, j]为从第 i 行第 j 列开始向右延伸高度均不小于 h[i, j]的最右 一列的列号,即从第 j 列开始至该列的每个元素的高度均不小于 h[i,j],则底边 包含元素 a[i,j]的面积最大的全零子阵的面积为(r[i,j]-l[i,j]+1)*h[i,j],而 h(i,j),l(i,j),r(i,j)可以逐行递推,递推的方法为增加一个元素值全 1 的第 0 行,这不影响问题的解,则根据定义 h[0,j]=0, l[0,j]=1,r[0,j]=n,其中 1<=j<=n,此为边界条件,且对任意的非零元素上式均适用;再假设第 i 行包含第 j 列的最长的连续全零段最左为第 c1 列,最右为第 c2 列,即第 i 行从第 c1 列开始至第 c2 列为止的每个元素的高度均不小于 h[i,j]f[i,j]:=min(f[i-1,j],v[i,j-1],v[i-1,j-1])+1; ans:=maxvalue(f);

最大子矩阵 2 最大带权 01 子矩阵
-----最大带权 01 子矩阵 O(n^2*m) 枚举行的起始,压缩进数列,求最大字段和,遇 0 则清零


最大子矩阵 3 最大字段和问题

-----最大字段和问题 f:=max(f[i-1]+b,b); f[1]:=b[1]

最大子矩阵问题 4 最大子立方体问题

-----最大子立方体问题 枚举一组边 i 的起始,压缩进矩阵B[I,j]+=a[x,I,j] 枚举另外一组边的起始,做最大子矩阵
最大子矩阵问题 5 居住空间

-----居住空间 f[i,j,k]:=min(min(min(f[i-1,j,k],f[i,j-1,k]),min(f[i,j,k-1],f[i-1,j-1,k])), min(min(f[i-1,j,k-1],f[i,j-1,k1]),f[i-1,j-1,k-1]))+1;

判定性问题
判定性问题 1 能否被 4 整除

-----能否被 4 整除 g[1,0]:=true;g[1,1]:=false; g[1,2]:=false; g[1,3]:=false; g[i,j]:=g[i-1,k] and ((k+a[i,p])mod 4 = j)
判定性问题 2 能否被 k 整除

-----能否被 k 整除 f[I,j±n modk]:=f[i-1,j]; 数字三角形 -k<=j<=k; 1<=i<=n


数字三角形 1 朴素数字三角形
-----朴素の数字三角形f[i,j]:=max(f[i+1,j]+a[I,j],f[i+1,j+1]+a[i,j]);

数字三角形 2 晴天小猪历险记
-----晴天小猪历险记之 Hill 同一阶段上暴力动态规划if[i,j]:=min(f[i,j-1],f[I,j+1],f[i-1,j],f[i-1,j-1])+a[i,j]

数字三角形 3 小胖办证
-----小胖办证f[i,j]:=max(f[i-1,j]+a[i,j],f[i,j-1]+a[i,j],f[i,j+1]+a[i,j])

数字三角形 4 过河卒
描述 Description 棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点 有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。 棋盘用坐标表示, 点(0,0)、 点(n, m)(n, m 为不超过 15 的整数), A B 同样马的位置坐标是需要给出的。 现在要求你计算出卒从 A 点能够到达 B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。 输入格式 Input Format 一行四个数据,分别表示 B 点坐标和马的坐标。输出格式 Output Format 一个数据,表示所有的路径条数。 样例输入 Sample Input 6633 样例输出 Sample Output 6

数字三角形 5 朴素的打砖块
-----朴素的打砖块f[i,j,k]:=max(f[i-1,j-k,p]+sum[i,k],f[i,j,k]);

数字三角形 6 优化的打砖块
-----优化的打砖块 f[I,j,k]:=max{g[i-1,j-k,k-1]+sum[I,k]}


状态压缩动态规划

状态压缩动态规划 1---炮兵阵地
背景 Background 北京奥运会开幕了,这是中国人的骄傲和自豪,中国健儿在运动场上已经创造了一个又一个辉煌, super pig 也不例外⋯⋯⋯⋯⋯⋯ 描述 Description 司令部的将军们打算在 N*M 的网格地图上部署他们的炮兵部队。一个 N*M 的地 图由 N 行 M 列组成,地图的每一格可能是山地(用“H” 表示), 也可能是平原(用“P”表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队); 一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 00000000 00001000 0000100000112110 00001000 00001000 00000000 2 表示士兵,1 表示可攻击到的区域如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域: 沿横向左右各两格,沿纵向上下各两格。 图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击, 即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内) ,在整个地图区域内最多能够摆放多少我军的炮兵部队。输入格式 Input Forma 第一行包含两个由空格分割开的正整数,分别表示 N 和 M; 接下来的 N 行,每一行含有连续的 M 个字符(?P?或者?H?),中间没有空格。按顺序表示地图中每一行的数据。 N≤100;M≤10。 输出格式 Output Format 仅在第一行包含一个整数 K,表示最多能摆放的炮兵部队的数量。例样输入 Sample Input

5 4


PHPP PPHH PPPP PHPP PHHP
样例输出 Sample Output 6

Max(f[Q*(r+1)+k],g[j]+num[k]) If (map and plan[k]=0) and ((plan[P] or plan[q])and plan[k]=0)

递推天地
递推天地 1 核电站问题

------核电站问题 一个核电站有 N 个放核物质的坑,坑排列在一条直线上。如果连续 M 个坑中放 入核物质,则会发生爆炸,于是,在某些坑中可能不放核物质。 任务:对于给定的 N 和 M,求不发生爆炸的放置核物质的方案总数。
一个核电站有 N 个放核物质的坑,坑排列在一条直线上。 如果连续 M 个坑中放入核物质,则会发生爆炸,于是,在某些坑中可能不放核物质 。 任务:对于给定的 N 和 M,求不发生爆炸的放置核物质的方案总数 Input 该题有多组测试数据,每组数据一行,两个正整数 N,M( 1<N<50,2≤M≤5) Output 每组数据只输出一个正整数 S,表示方案总数。 Sample Input 43 Sample Output 13 算法: 这道题似乎可以用容斥定理来做,但要实现几乎不可能,运算中的数据会很大,即使高精度也很慢很烦。我们很自然的想到了可以用动态规划算法。 我们开始试探两个元素的 dp,设:f[i,j]表示前 i 个坑放置 j 个核物质爆炸时的放置总方案数,那么似乎


f[i,j]= f[i-1,j-1]当 j>0在第 i 个坑放核物质 f[i-1,j]当 j=0不在第 i 个坑放核物质但程序编好后,发现结果不对,因为有情况没考虑到:如果在第 i 个坑放核物质,那么也许前 i-1个坑里 j-1个核物质放在最后连续 j-1个坑里,在这种情况下在第 i 个坑放核物质是不允许的! 我们只好再增加一个参数 k,f[i,j,k]表示前 i 个坑放置 j 个核物质爆炸且最后连续 放置 k 个核物质时的放置总方案数,那么 f[i,j,k]= f[i-1,j-1,k-1]??当 k>0 <l=0Sigma m> f[i-1,j,l] 当 k=0 最后用个二重循环统计一下,大功告成! 复杂度:很显然时间复杂度为 o(n^3),空间复杂度为 o(n^3),使用滚动数组可降一维。疑问: 虽然 dp 可以通过,但很可能有更好的算法(如公式) ,目前没有找到,但有一发 现: Ans(n,m)= 2^n-omega 当 n-m 不变,omega 为常数,问题是 omega 与 n-m 到底有什么关系?? program kk; varn,m,i,j:longint; a:array[0..50]of int64; begin assign(input,'nuclear.in');reset(input); assign(output,'nuclear.out'); rewrite(output); readln(n,m);a[0]:=1; for i:=1 to m do a[i]:=2*a[i-1]; dec(a[m]); for i:=m+1 to n doa[i]:=2*a[i-1]-a[i-m-1]; writeln(a[n]); close(input); close(output); end.

f[-1]:=1; f[0]:=1; f[i]:=2*f[i-1]-f[i-1-m]

递推天地 2 ------数的划分

------数的划分 整数 n 分成 k 份,且每份不能为空,任意两份不能相同(不考虑顺序)。 例如:n=7,k=3,下面三种分法被认为是相同的。


1,1,5; 1,5,1; 5,1,1;

问有多少种不同的分法。 f[i,j]:=f[i-j,j]+f[i-1,j-1];
var deep,u,last, n,k:integer; total:longint; proceduresearth(u,last,deep:integer); var i:integer; begin if (deep=k) then begintotal:=total+1; exit; end; for i:=last to u div 2 do searth(u-i,i,deep+1); end;begin read(n,k); searth(n,1,1); writeln(total); end.

#include <iostream> #include <vector> using namespace std; voidfun() { int n = 0,k=0; int j = 0; cout<<"请输入n 和 k 的值:";cin>>n>>k; vector<int> myvec; myvec.push_back(n-k+1); for (inti=1;i<k;i++) { myvec.push_back(1); } for (int m=k-1;m>=0;m--)cout<<myvec[m]<<" "; cout<<endl; while(j != k-1)


{ while(myvec[j] - myvec[j+1] > 1) { myvec[j] -= 1; myvec[j+1] += 1; for(int m=k-1;m>=0;m--) cout<<myvec[m]<<" ";cout<<endl; } j++; } } void main() { while (1) { fun(); } }

.递推天地 3 情书抄写员

Description
Wind 的女友数量是惊人的。每个月开始时,Wind 的每一个正式女友都会给 他介绍 k 个新的女生,我们称这样的新人为“潜在的女友”(Potential GirlFriend)。 通过近两个月的交往, 潜在的女友总会在下一个月末成为正式的女友,并在第三个月初开始每月介绍新的女友。 我们假设,在第一个月 Wind 只有一个潜在的女友。Wind 每个月都要给他 的所有女友和潜在女友(包括本月初刚介绍来的人)写一封情书。在第a 个月和 第 b 个月,Wind 有事不在,需要雇用一些“情书抄写员”来代替他完成这个操作。Wind 将会让每个情书抄写员负责 t 封情书, 并希望这个t 值可以使得第 a 个月和 第 b 个月的任务都能正好分尽。 Wind 拜托“实习生”佳佳计算出第 a 个月和第 b 个月各需要写多少情书。为 了雇用尽可能少的抄写员,Wind 还想知道 t 的最大值是多少。 由于答案可能非常大,因此你只需要输出这三个数值 mod m 的结果即可。

Input


输入数据一共只有一行,为四个用空格隔开的正整数,分别表示 k、m、a 和 b,其意义如题目描述。

Output
输出数据一共有三行,每行输出一个数。 第一行为第 a 个月Wind 需要写的情书数 mod m 的值; 第二行为第 b 个月 Wind 需要写的情书数 mod m 的值; 第三行为能同时整除第 a 个月的数目和第 b 个月的数目的最大值 mod m 的 结果。

Sample Input
5 4 3 6

Sample Output
2Hint 、 样例说明 第一个月只有一个潜在女友,第二个月该女友渐渐发展成熟,直到第三 个月正式成为女友并介绍了 5 个新的女生。因此,第三个月需要写 6 封情书,输 出 6 mod 4 的结果,即 2; 第四个月将再增加 5 人使得总人数达到 11,到第五个月时将增加 30 个 人。这样推下去可以得到,第六个月需要写 96 封情书。数值 6 是能整除 6 和 96 的最大值。我们输出 mod 4 的结果,即 0 和 2。数据规模 对于 30%的数据,a,b,k ≤ 10; 对于 80%的数据,a,b,k ≤ 100 000; 对于 100%的数据,a,b,k ≤ 1 000 000 000,m ≤ 2^31 - 1。

Source
0 2


递推天地 4 错位排列

-----错位排列 f:=(i-1)(f[i-2]+f[i-1]);f[n]:=n*f[n-1]+(-1)^(n-2);

递推天地 5 直线分平面最大区域数

-----直线分平面最大区域数 f[n]:=f[n-1]+n :=n*(n+1) div 2 + 1;

递推天地 6 折线分平面最大区域数

-----折线分平面最大区域数 f[n]:=(n-1)(2*n-1)+2*n;

递推天地 7 封闭曲线分平面最大区域数

-----封闭曲线分平面最大区域数 f[n]:=f[n-1]+2*(n-1) :=sqr(n)-n+2;
递推天地 8 凸多边形分三角形方法数

-----凸多边形分三角形方法数 f[n]:=C(2*n-2,n-1) div n; 对于 k 边形 f[k]:=C(2*k-4,k-2) div (k-1);//(k>=3)

递推天地 9 Catalan 数列一般形式

-----Catalan 数列一般形式 1,1,2,5,14,42,132 f[n]:=C(2k,k) div(k+1);

递推天地 10 彩灯布置

彩灯布置
一、题意简述 有 n 盏位置固定的彩灯排列成圆形,要用 m 种颜色去着色,求使相邻的彩灯着不同颜色的方案数。 47.合唱队形(chorus.pas)


N 位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的 K 位同 学排成合唱队形。 合唱队形是指这样的一种队形: K 位同学从左到右依次编号为 1, 设 2?, K, 他们的身高分别为 T1,T2,?,TK, 则他们的身高满足T1<...Ti+1>?>TK(1<=i<=K)。 你的任务是,已知所有 N 位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。 【输入文件】 输入文件 chorus.in 的第一行是一个整数 N(2<=N<=100),表示同学的总数。 第一行有 n 个整数,用空格分隔,第 i 个整数 Ti(130<=Ti<=230)是第 i 位同学 的身高(厘米)。【输出文件】 输出文件 chorus.out 包括一行,这一行只包含一个整数,就是最少需要几 位同学出列。 【样例输入】 8 186 186 150 200 160 130 197 220 【样例输出】 4 【数据规模】对于 50%的数据,保证有 n<=20; 对于全部的数据,保证有 n<=100。 排列组合中的环形染色问题f[n]:=f[n-1]*(m-2)+f[n-2]*(m-1);

(f[1]:=m; f[2]:=m(m-1);

递推天地 11-----盒子与球

-----盒子与球 f[i,1]:=1; f[i,j]:=j*(f[i-1,j-1]+f[i-1,j]);


树型动态规划

树型动态规划 1 加分二叉树

-----加分二叉树 (从两侧到根结点模型)
设一个 n 个节点的二叉树 tree 的中序遍历为(l,2,3,?,n) 其中数字 1,2,3,?,n , 为节点编号。每个节点都有一个分数(均为正整数),记第 i 个节点的分数为 di, tree 及它的每个子树都有一个加分,任一棵子树 subtree(也包含 tree 本身)的加 分计算方法如下: subtree 的左子树的加分× subtree 的右子树的加分+subtree 的根的分数 若某个子树为空,规定其加分为 1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。 试求一棵符合中序遍历为 (1,2,3,?,n) 且加分最高的二叉树 tree。 要求输出; (1)tree的最高加分 (2)tree 的前序遍历

【输入格式】 第 1 行:一个整数 n(n<30),为节点个数。 第 2 行:n 个用空格隔开的整数, 为每个节点的分数(分数<100)。 【输出格式】第 1 行:一个整数,为最高加分(结果不会超过 4,000,000,000)。第 2 行: n 个用空格隔开的整数,为该树的前序遍历。 【输入样例】 5 5 7 1 2 10 【输出样例】 145 3 1 2 4 5 【分析】 F[I,j]:=max{f[I,k-1]*f[k+1,j]+c[k]} =========题目解析======== 16 题目补充点一:我在网上找到的题目中有这么一句话“若某个子树为主,规定其加分为 1”,这句话应该是“若某个子树为空,规定其加分为 1”。 17 题目补充点二:如果有一颗二叉树形如图 1,那么它的中序遍历为 bac,前 序遍历为 abc,后序遍历为 bca。 18 a19 / \ 20 b c 21 (图 1)


22 题目补充点三:输入样例所对应的二叉树如图 2,请读者写出图 2 的中序遍 历与前序遍历,算出得分,并与题目对照是否正确,确保读懂题意! 23 324 / \ 25 1 4 26 \ \ 27 2 5 28 (图 2) 29 题目补充点四:任何分数均不会出现负值。 30 31 32 明确上面几点后现在我们来考虑如何做这道题,首先要看到一点:题 目给出的是中序遍历,并且遍历号为 1、2、3、4、??、n,那么如果 i 是树的 根,则所有编号小于 i 的节点在树中的位置均位于 i 的左边,所有编号大于 i 的节点均位于 i 的右边。 33 根 据这点我们来考虑更为一般性的定理。定理一:如果有一颗子树,他的中序遍历为 a1、 a2、 a3、 ??、 (“a”后面的字符看成下标, an 如出现 ai-1 时,下标是i-1,表示第 i-1 个数,而不是第 i 个数减去 1,下同),且子树的 根为 ai,那么a1、a2、a3、??、ai-1 均位于 ai 的左边,ai+1、ai+2、??、 an 均位于 ai 的右边。 3435 36 根 据定理一,题目给出一个中序遍历为 1、2、3、??、n(这里的数 字表示节点的编号,后面提到节点 i 及表示编号为 i 的节点)的二叉树时我们可 以任意假定某个节点 i 为树的根而不会打乱二叉树的中序遍历,因为我们只要 在二叉树中把编号小于 i 的节点都放在 i 的左边,编号大于i 的节点都放在 i 的右边即可,而凑巧的 是,在中序遍历序列中也有这一性质(及在中序遍历序 列中 i 左边的数均小于 i,i 右边的数均大于 i),这为我们使用动态规划方法 来解此题提供了可能性。 37 更 进一步考虑,如果我们假设节点 i 为整个树的根,那么节点 1、2、 3、??、i-1 就组成了根节点的左子树,同样节点 i+1、i+2、??、n 组成了 根节点 的右子树。 此时我们又可以假设在左子树中节点 j (j<i) 是左子树的根, 再次运用定理一可知, 这种假设不会打乱整个树的中序遍历。同样对于右子树也 成立。 38 39 40 下面我们来考虑动态规划的方法,我们暂时不考虑输出前序遍历的问 题(关于动态规划的更多信息请参考《算法导论》第 15 章): 41 考虑一:最优子结构。 42 对 于一颗中序遍历为 a1、a2、??、an 的加分二叉树,我们虽然不 知道究竟哪个节点才是得分最大的二叉树的根节点,但是我们可以每一个节点都 试一下,看看 那个节点作为根节点时能够获得的分数最大。比如当我们考虑把 节点 ai 作为根节点,那么此时摆在我们面前的问题就是左、右子树中的根又是哪个节点呢?答案 是:左子树的根节点是作为根时能够让左子树得分最大的节


点, 右子树的根节点是作为根时能够让右子树得分最大的节点。 请读者自己证明! 43 那么加分二叉树的最优子结构可以表述如下:当选择某节点为根时,如果要获得最大的分数, 那么根的左节点必使得左子树能够获得最大分数,同样 根的右节点必使得右子树能够获得最大分数。 4445 46 考虑二:重叠子问题。 47 根 据上面考虑的最优子结构性质,对于给出一个二叉树的中叙遍历求最大得分问题, 我们需要考虑的仅有三个小问题,第一是如何选择一个节点作为 树的根使得得分最 大,第二是如何在左子树中选择一个节点作为左子树的根使 得左子树的得分最大,第三个问题是如何在右子树中选择一个节点作为右子树的 根使得右子树的得分最 大。很明显,问题二和问题三是问题一的重叠子问题。 根据重叠性质可知,如果我们用函数 MaxTreeScore(left,right)表示一个中序 遍历 为 left、left+1、left+2、??、right 的二叉树所能得到的最大加分, 用变量 nodeScore[i]表示编号为 i 的节点的分数,那么 就有公式 48MaxTreeScore(left,right) = max{ MaxTreeScore(left,middle1) *MaxTreeScore(middle+1,right) + nodeScore[middle] },其中minddle 从 left 递增 循环到 right。这一点请读者自己证明! 49 50 51 考虑三:子问题独立。 52 根据上面的分析,子问题便是问题二和问题三,所谓子问题独立就是说问题二的解决不会影响到问题三的解决,这一点显然成立,请读者自己证明! 53 54 55 考虑四:做备忘录。 56 在 程序运行中可能多次使用相同的参数调用 MaxTreeScore 函数,比如某时刻调用了 MaxTreeScore(3,21),另一时刻再次调用MaxTreeScore(3,21), 很明显两次调用得到的结果是相同的,因此可以用一个全 局变量来保存这个值,当第一次调用时算出函数值保存起来,第二次调用时直 接获得此值而不用再次计算,这样可以节约大量的时间!程序中我们用 maxTreeScore[left][right]来保存 MaxTreeScore(left,right)的值,注意,开 头为大写字母的是函数, 开头为小写字母的是变量,在我写的代码中均使用此规则! 57 58 59 考虑五:边界。 60 MaxTreeScore 函数会调用自己,但也不能每一次都调用自己,要不就会出现死循环调用, 那么什么时候才是个头呢?这就是边界,跟据题意“叶子的 加分就是叶节点本身的分数” 可知当代如 MaxTreeScore 函数的两个参数相等 时就是求叶节点加分的情况,即 MaxTreeScore(x,x)就为 x 节点的分数 nodeScore[x],根据题意“若某个子树为空,规定其加分为 1”可知,当代如 MaxTreeScore 函数的第一个参数大于第二个参数时此树为空(不存在),即 MaxTreeScore 等于 1。


61 62 63 考虑六:所求。 64 因为我们要求的是整个树能够得到的最大分数,根据上面的分析可知题目要求我们求出的最大分数即为 MaxTreeScore(1,n)。注意,如果我们从 0 开 始给节点编号的话所求就相应地为 MaxTreeScore(0,n-1),并且程序中就是这样做的! 65 66 67 最后,我们来考虑如何输出一个分数获得最大的二叉树的前序遍历, 遍历二叉树需要知道些什么?遍历二叉树需要知道些什么?遍历二叉树需要知道些什么?请读者先想一想! 68 69 只 要给我们任意一个子树(包括整棵树)的中序遍历我们都知道这个 子树的跟节点是哪个的话,便可以前序遍历整个二叉树! (其实不仅是前序遍历, 中序遍历、后序 遍历都行!这一点请读者自己证明。)比如我们用Tree{left,right}表示中序遍历为 left、left+1、left+2、??、right 的二叉树,用 root[left][right]保存这颗树获得最大分数时的根节点,那么对 Tree{left,right}进行前序遍历时及为先遍历跟 节点root[left][right],然 后遍历左子树Tree{left,root[left][right]-1},最后遍历右子树Tree{root[left][right]+1,right}。这一点类似于上面考虑的重叠子问题。 如果用数组value[i,j]表示从节点 i 到节点 j 所组成的二叉树的最大加分,则动态方程可以表示如下:value[i,j]=max{value[i,i]+value[i+1,j],value[i+1,i+1]+value[i,i]*valu e[i+2,j],value[i+2,i+2]+value[i,i+1]*value[i+3,j],?,value[j-1,j-1]+value[i,j2]*value[j,j],value[j,j]+value[i,j-1]} 题目还要求输出最大加分树的前序遍历序列, 因此必须在计算过程中记下从 节点 i 到节点 j 所组成的最大加分二叉树的根节点,用数组 root[i,j]表示 很显然,本题适合用动态规划来解。用样例数据具体分析我们可以画出如下几 种不同结果的树:

2 ((5*1)+7)*10+2=122

图1 图3 (7+5)*(10+2)+1=145



5*(1*10+2)+7=67


显然输出结果是图2。 如果用数组 f[i,j]表示从节点 i 到节点 j 所组成的二叉树的最大加分,用数组 root[i,j]表示从节点 i 到节点 j 所组成的最大加分二叉树的根节点。程序的关 键部分如下: For I:=1 to n do For j:=0 to n do begin r[i,j]:=0; f[i,j]:=1; end;For I:=1 to n do begin read(f[i,i]); r[i,i]:=i; end; For k:=1 to n-1 do ForI:=1 to n-k do begin J:=I+k; For p:=I to j do If f[i,j]< f[i,p-1]* f[p+1,j]+f[p,p] then Begin f[i,j]= f[i,p-1]* f[p+1,j]+ f[p,p]; r[i,j]:=p; end; 本题考查了二叉树的遍历和动态规划法。最大加分二叉树的前序遍历序列可能不 唯一。

树型动态规划 2 选课

-----选课 (多叉树转二叉树,自顶向下模型)
在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习, 在课程里有些课程必须在某些课程之前学习, 如高等数学总是在其它课程之前学习。现在有 N 门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程 a是 课程 b 的先修课即只有学完了课程 a,才能学习课程 b)。一个学生要从这些课程里 选择 M 门课程学习,问他能获得的最大学分是多少?

F[I,j]表示以 i 为根节点选 j 门功课得到的最大学分


f[i,j]:=max{f[t[i].l,k]+f[t[i].r,j-k-1]+c[i]} 问题 描述Description 学校实行学分制。 每门的必修课都有固定的学分,同时还必须获得相应的选修课 程学分。学校开设了 N(N<300)门的选修课程,每个学生可选课程的数量 M 是 给定的。学生选修了这 M 门课并考核通过就能获得相应的学分。 在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须 在选了其它的一些课程的基础上才能选修。例如《Frontpage》必须在选修了《Windows 操作基础》 之后才能选修。 我们称 《Windows 操作基础》《Frontpage》 是 的先修课。每门课的直接先修课最多只有一门。两门课 也可能存在相同的先修 课。每门课都有一个课号,依次为 1,2,3,?。 例如:

表中 1 是 2 的先修课,2 是 3、4 的先修课。如果要选 3,那么 1 和 2 都一定已被选修过。 你的任务是为自己确定一个选课方案,使得你能得到的学分最多,并 且必须满足先修课优先的原则。假定课程之间不存在时间上的冲突。 输入格式 Input Format 输入文件的第一行包括两个整数 N、M(中间用一个空格隔开)其中 1≤N≤300,1≤M≤N。 以下 N 行每行代表一门课。课号依次为 1,2,?,N。每行有两个数(用一个空 格隔开),第一个数为这门课先修课的课号(若不存在先修课则该项为 0),第 二个数为这门课的学分。学分是不超过 10 的正整数。 输出格式 Output Format 输出文件只有一个数,实际所选课程的学分总数。分析 根据题目描述我们可以知道这是个树形动规问题,但由于一个点可以有多个儿 子,很难写出方程,所以我们想到将森林转二叉,之后再去做。


这里先补充一下多叉转二叉的知识,其规则是左儿子,右兄弟,即左子树上的都 是儿子节点,右子树上的都是兄弟节点。这道题是森林,我们只需要虚拟零节点 即可。我们要搜索的根就是 f[0].left; 粘个小代码 type cc=recordl,r:longint; end; var tree:array[1..1000] of cc; a:array[1..1000,1..1000] oflongint; m,i,x,y,z,t:longint; begin readln(m); for i:=1 to m do beginreadln(x,y,z); a[x,y]:=z; if tree[x].l=0 then tree[x].l:=y; t:=tree[x].l;while(tree[t].r<>0)do t:=tree[t].r; tree[t].r:=y; end; end. 转化为二叉树之后,我们很容易就能写出方程 f[i,j]表示以 i 为根的树,选了 j 门课能够得到的最大学分。 f[i,j]:=max{f[tree[i].left,k]+f[tree[i].right,j-1-k]+a[i],f[tree[i].right,j]}只和当前这门课选不选有关,这是很显然的,完全根据我们转换出的二 叉树的定义和题目要求得来。 反思 在树的儿子很多,很难处理时候,我们可以考虑多叉转二叉,或者森林转二叉。简化方程,只和当前节点选或者不选有关。特别注意,这样转的话会加大树的深 度,也就是我们压栈的次数,数据范围极大时慎用。 记忆化搜索是解决树归的利器,注意记忆化搜索的框架,和打法。一定要注意细节。 在函数中应有当前状态是否已知的判断, 边界条件的处理 (注意给数组赋值) , 根据方程枚举状态找最优,最后将求出的最优赋到数组中再返回值。 code


program liukee; type lkj=record l,r,num:longint; end; varf:array[0..1000,0..1000] of longint; tree:array[0..1000] of lkj; n,m:longint;procedure init;//读入数据,多叉转二叉 var i,x,y,t:longint; beginreadln(n,m); for i:=1 to n do begin readln(x,tree[i].num); if tree[x].l=0 thentree[x].l:=i else begin t:=tree[x].l; while(tree[t].r<>0) dot:=tree[t].r; tree[t].r:=i; end; end; end; functionfind(i,m:longint):longint;//记忆化搜索 var j,temp1,temp2:longint;begin if f[i,m]<>-1 then exit(f[i,m]); if(i=0)or(m=0)then beginf[i,m]:=0; exit(0); end; temp1:=find(tree[i].r,m); for j:=0 to m-1 do begintemp2:=find(tree[i].l,j)+find(tree[i].r,m-1-j)+tree[i].num; if temp2>temp1then temp1:=temp2; end;


f[i,m]:=temp1; exit(f[i,m]); end; begin fillchar(f,sizeof(f),$ff); init;writeln(find(tree[0].l,m)); end.

树形动态规划 3 贪吃的九头龙

-----贪吃的九头龙
传说中的九头龙是一种特别贪吃的动物。虽然名字叫“九头龙”,但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于九,当然也会有旧头因衰老而自己脱落。 有一天,有 M 个脑袋的九头龙看到一棵长有 N 个果子的果树,喜出望外,恨不得一口把它全部吃掉。 可是必须照顾到每个头,因此它需要把 N 个果子分成 M 组,每组至少有一个果子,让每个头吃一组。 这 M 个脑袋中有一个最大,称为“大头”,是众头之首,它要吃掉恰好 K 个果子,而且 K 个果子中理所 当然地应该包括唯一的一个最大的果子。果子由 N-1 根树枝连接起来,由于果树是一个整体,因此可以从 任意一个果子出发沿着树枝“走到”任何一个其他的果子。 对于每段树枝,如果它所连接的两个果子需要由不同的头来吃掉,那么两个头会共同把树枝弄断而把 果子分开;如果这两个果子是由同一个头来吃掉,那么这个头会懒得把它弄断而直接把果子连同树枝一起吃掉。当然,吃树枝并不是很舒服的,因此每段树枝都有一个吃下去的“难受值”,而九头龙的难受值就是所有头吃掉的树枝的“难受值”之和。 九头龙希望它的“难受值”尽量小,你能帮它算算吗?

题目简述:将一棵树中的节点染成 M 种颜色,每个节点有且只有一种颜色, 在满足以下条件下使得两端颜色相同的边的权值和最小,所有边权均非负。(1) 必须有 K 个 1 号颜色的点;(2)1 号节点必须是 1 号颜色;(3)每种颜色必须 至少有一个节点。如无解,输-1。 无解的情况很明显,当且仅当 N-K<M-1 时无解。 考虑用动态规划来解决。 如果以一棵子树作为一个子结构,分析需要考虑的状态: (1)根节点的颜色。(2)1 号颜色的个数。(3)树中颜色的分配情况, 如何保证每种颜色都有节点。 初步分析可以得到一种四维的状态:


f[i][j][k][w],表示在以 i 为根的子树中,有 j个 1 号节点,根染 k 号颜 色,树中已有的颜色用 w 表示(w 是一个二进制数)的状态下最小的权值和。 首先,这个方程用到了状态压缩 w,因此对于本题 300 的数据范围是不现实 的,需要继续思考。 假设这样一个问题,仍然是对树染色,可以任意染色,那么只要 2 种颜色, 就可以保证任意一条边两端的颜色不同, 联想到这道题, 因为 1 号颜色比较特殊,因此单独处理,而余下的颜色如果大于等于 2 种,那么无论 1 号颜色如何染色,都可以保证一条边两边不会出现相同的非 1 号颜色的情况,换言之,如果M>=3, 对答案有贡献的只有 1 号颜色节点之间的边。这样当 M>=3 时,可以直接按 3 处 理,这样状态压缩是可以承受的。既然有了这样的优化,k 也可以只用 0,1 来表 示,1 表示 1 号颜色,0 表示非 1 号颜色。而 M=2 时就更简单了,0,1 就直接把 颜色分开了。 初步分析下来,得到了一个状态数为 O(N*K*2*2?),转移为 O(K*2*2?),总 复杂度为 O(N*K?*256)。由于 N,K≤300,理论分析是会超时,但实际操作中可以不用循环到 K, 因为循环的上限可以设为 min(K,子树 i 的总节点数)。这样的话, 这个复杂度还是可以承受的。 本题还有优化吗?答案是肯定的。如果要优化状态,前 3 维似乎是无法优化的,考虑第 4 维。之所以一开始要加入这一维,就是担心会存在有一些颜色无法染上的问题,经过后来的分析,发 现除了 1 号颜色会对答案有贡献之外,其他颜色其实是可以被忽略的,因为我们可以保证它们不会对答案造成影响,那么只要有足够多的节点来染除 1 外的颜 色,就可以确保每一种颜色都可以被染上,至于到底在哪里,其实并不重要。这样想,就会发现其实第四维是完全多余的,可以直接略去。 最终状态:f[i][j][k], 表示在以 i 为根的子树中,有 j 个 1 号节点,根染 k 号颜色的状态下最小的权值和。 注意:在具体转移时,要先将原来的 f[i]复制一份,然后将 f[i]清为无穷 大,然后用备份的 f[i]结合上述方程来计算 f[i]的值。因为计算儿子没计算完 时的状态是不能保留的,因为他们没有考虑完所有的情况,严格来讲,可以在上 述方程中加上 1 维, 表示计算到当前根的第几个儿子,每一次都是有上一次的状 态和儿子的最终状态合并。这样就可以用滚动数组或辅助数组的方式来实现。设 y 是 i 的一个儿子。

f[i][j][1]=min{f[i][j][1],f[y][k][1]+ pref[i][j-k][1]+value[i][y],f[y][k][0]+pref[i][j-k][1]} f[i][j][0]=min{f[i][j][0],


f[y][k][0]+ pref[i][j-k][0]+ value[i][y] |if m=2 f[y][k][0]+ pref[i][j-k][0]+value[i][y] | if m≠2 f[y][k][1]+ pref[i][j-k][0] }
初始值,对于每一个 i,f[i][0][0]=f[i][1][1]=0.答案为 f[1][K][1]。 *Author:rj; *Problem:NOI2002 dragon *Language:C++; *state:Solved 2010.6.26 20:20 *Memo:Treedp 背包 #include<iostream> #include<cstdio>#include<cstring> #include<algorithm> #include<cstdlib> usingnamespace std; const int MaxN=305,MaxK=305,oo=0x2f2f2f2f; int N,M,K; int f[MaxN][MaxK][2],tmp[MaxK][2];inth[MaxN]={0},next[MaxN<<1],data[MaxN<<1],value[MaxN<<1],cnt,sum[MaxN]={0};


void AddEdge(int x,int y,int z) {next[++cnt]=h[x];h[x]=cnt;data[cnt]=y;value[cnt]=z; } inline void Min(int&a,int b,int c){if(a>b)a=b;if(a>c)a=c;} void DP(int x,int from) {f[x][1][1]=0;f[x][0][0]=0;sum[x]=1; int p,y,j,k,t; for (p=h[x];p;p=next[p])if((y=data[p])!=from) { DP(y,x); sum[x]+=sum[y]; if(M==2)t=value[p];else t=0;memcpy(tmp,f[x],sizeof(f[x])); memset(f[x],0x2f,sizeof(f[x])); for (j=sum[x];j>=0;j--){ if(j>0) for(k=j-1;k>=0;k--)Min(f[x][j][1],tmp[j-k][1]+f[y][k][0],tmp[j-k][1]+f[y][k][1]+value[p]);


for (k=j;k>=0;k--)Min(f[x][j][0],tmp[j-k][0]+f[y][k][0]+t,tmp[j-k][0]+f[y][k][1]); } } } int main() { int i,x,y,z; memset(f,0x2f,sizeof(f));scanf("%d%d%d",&N,&M,&K);if(N-K<M-1){printf("-1\n");return 0;} cnt=1; for (i=1;i<N;i++){ scanf("%d%d%d",&x,&y,&z); AddEdge(x,y,z);AddEdge(y,x,z); } DP(1,0); printf("%d\n",f[1][K][1]); return 0; }


树形动态规划 4 有线电视网

-----有线电视网 f[i,p]:=max(f[i,p],f[i,p-q]+f[j,q]-map[i,j])leaves>=p>=l, 1<=q<=p;

树形动态规划 5 有向树 k 中值问题

-----有向树 k 中值问题F[I,r,k]:=max{max{f[l,I,j]+f[r,I,k-j-1]},f[f[l,r,j]+f[r,r,k-j]+w [I,r]]}

树形动态规划 6 聚会的快乐

-----聚会的快乐 f[i,2]:=max(f[i,0],f[i,1]);f[i,1]:=sigma(f[t^.son,0]); f[i,0]:=sigma(f[t^.son,3]);

树形动态规划 7 .皇宫看守

太平王世子事件后,陆小凤成了皇上特聘 的御前一品侍卫。皇宫以午门为起点,直到后宫 嫔妃们的寝宫,呈一棵树的形状;某些宫殿间可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有 人全天候看守,在不同的宫殿安排看守所需的费用不同。可是陆 小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。 请你编程计算帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。输入数据:输入数据由文件名为 intput.txt 的文本文件提供。输入文件中数据表示一棵树, 描述如下: 第 1 行 n,表示树中结点的数目。 第2 行至第 n+1 行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号 i(0<=n) ,在 该宫殿安置侍卫所需的经费 k,该边的儿子数 m,接下来 m 个数,分别是这个节点的 m 个 儿子的标号 r1,r2,...,rm。 对于一个 n(0 < n <= 1500)个结点的树,结点标号在 1 到 n 之间,且标号不重复。 输出数据:输出到 output.txt 文件中。输出文件仅包含一个数,为所求的最少的经费。如右图的输入数据示例: Sample Input


6 1 30 3 2 3 4 2 16 2 5 6 350 440 5 11 0 650 Sample Output 25

树形动态规划 8 -----APIO2007 风铃

-----APIO2007 风铃 f:=f[l]+f[r]+{1 (if c[l]<c[r])}g:=1(d[l]<>d[r]) 0(d[l]=d[r]) g[l]=g[r]=1 then Halt;

树形动态规划 9-----CTSC 2001 选课

-----CTSC 2001 选课
描述 Description 学校实行学分制。每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分。学校开设了 N (N<300)门的选修课程,每个学生可选课程的数量 M 是给定的。学生选修了这 M 门课并考核通过就能获 得相应的学分。 在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才能选修。例如《Frontpage》必须在选修了《Windows 操作基础》之后才能选修。我们称《Windows 操作基础》是《Frontpage》的先修课。每门课的直接先修课最多只有一门。两门课也可能存在相同的先修课。每门课都有一个课号,依次为 1,2,3,⋯。例如: 表中 1 是 2 的先修课,2 是 3、4 的先修课。如果要选 3,那么 1 和 2 都一定已被选修过。不存在时间上的冲突。 输入格式 Input Format 输入文件的第一行包括两个整数 N、M(中间用一个空格隔开)其中1≤N≤300,1≤M≤N。 以下 N 行每行代表一门课。课号依次为 1,2,⋯,N。每行有两个数(用一个空格隔开),第一个数为这门课先修课的课号(若不存在先修课则该项为 0),第二个数为这门课的学分。学分是不超过 10 的正整数。 输出格式 Output Format 输出文件只有一个数,实际所选课程的学分总数。 你的任务 是为自己确定一个选课方案,使得你能得到的学分最多,并且必须满足先修课优先的原则。假定课程之间


样例输入 Sample Input

7 2 0 0 2 7 7 2

4 2 1 4 1 1 6 2

样例输出 Sample Output

13

树形动态规划 10(双次记录)

-----NOI2003 逃学的小孩、Chris 家的电话铃响起了,里面传出了 Chris 的老 师焦急的声音:“喂,是 Chris 的家长吗?你们的孩子又没来上课, 不想参加考试了吗?”一听说要考试, Chris 的父母就心急如焚,他们决定在尽量短的时间内找到 Chris。他们告诉 Chris 的 老 师:“根据以往的经验,Chris 现在必然躲在朋友 Shermie 或 Yashiro 家里偷 玩《拳 皇》 游戏。 现在, 我们就从家出发去找 Chris,一但找到, 我们立刻给您打电话。 ” 说完砰的一声把电话挂了。 Chris 居住的城市由 N 个居住点和若干条连接居住点的双向街道组成,经过 街道 x 需花费 Tx 分钟。可以保证,任两个居住点间有且仅有一条通路。Chris 家 在点 C,Shermie 和 Yashiro分别住在点 A 和点 B。Chris 的老师和 Chris 的父 母 都有城市地图, Chris 的父母知道点 A、 C 的具体位置而 Chris 的老师不知。 但 B、 为了尽快找到 Chris,Chris 的父母会遵守以下两条规则: ??如果 A 距离 C 比 B 距离 C 近,那么 Chris 的父母先去 Shermie 家寻找 Chris,如果找不到,Chris 的父母再去 Yashiro 家;反之亦然。


??Chris 的父母总沿着两点间唯一的通路行走。 显然,Chris 的老师知道 Chris 的父母在寻找 Chris 的过程中会遵守以上两条 规则,但由于他并不知道 A,B,C 的具体位置,所以现在他希望你告诉他,最坏情况下 Chris 的父母要耗费多长时间才能找到 Chris? A Shermie Chris Yashiro 1 1 C B 1 A 例如上图,这座城市由 4 个居住点和 3 条街道组成,经过每条街道均需花费 1 分钟时间。假设 Chris 住在点C,Shermie 住在点 A,Yashiro 住在点 B,因为 C 到 B 的距离小于 C 到 A 的距离,所以 Chiris 的父母会先去 Yashiro 家寻找 Chris, 一旦找不到,再去 Shermie 家寻找。这样,最坏情况下 Chris 的父母需要花费 4 分钟的时间才能找到 Chris。

朴素的话枚举节点 i 和离其最远的两个节点 j,kO(n^2) 每个节点记录最大的两个值,并记录这最大值分别是从哪个相邻节点传过

来的。当遍历到某个孩子节点的时候,只需检查最大值是否是从该孩子节


点传递来的。如果是,就取次大,否则取最大值

树形动态规划 11(完全二叉树) NOI2006 网络收费

-----NOI2006 网络收费【问题描述】 网络已经成为当今世界不可或缺的一部分。每天都有数以亿计的人使用网络 进行学习、科研、娱乐等活动。然而,不可忽视的一点就是网络本身有着庞大的运行费用。所以,向使用网络的人进行适当的收费是必须的,也是合理的。 MY 市NS 中学就有着这样一个教育网络。网络中的用户一共有 2N 个,编号 依次为 1, 2, 3, ⋯, 2N。这些用户之间是用路由点和网线组成的。用户、路由点与 网线共同构成一个满二叉树结构。树中的每一个叶子结点都是一个用户,每一个非叶子结点(灰色)都是一个路由点,而每一条边都是一条网线(见下图,用户 结点中的数字为其编号)。 MY 网络公司的网络收费方式比较奇特,称为“ 配对收费 ”。即对于每两个 用户i, j (1≤i < j ≤2N ) 进行收费。由于用户可以自行选择两种付费方式 A、B 中的一种,所以网络公司向学校收取的费用与每一位用户的付费方式有关。该费 用等于每两位不同用户配对产生费用之和。 为了描述方便,首先定义这棵网络树上的一些概念:祖先:根结点没有祖先,非根结点的祖先包括它的父亲以及它的父亲的祖先; 管辖叶结点:叶结点本身不管辖任何叶结点,非叶结点管辖它的左儿子所管 辖的叶结点与它的右儿子所管辖的叶结点;距离:在树上连接两个点之间的用边最少的路径所含的边数。 对于任两个用户 i, j (1≤i<j≤2N ),首先在树上找到与它们距离最近的公共祖先:路由点 P,然后观察 P 所管辖的叶结点(即用户)中选择付费方式 A 与 B 的人数,分别记为 nA 与 nB,接着按照网络管理条例第 X 章第Y 条第 Z 款进行收 费(如下表),其中 Fi, j 为 i 和 j 之间的流量,且为已知量。i 付费方式 j 付费方式 nA 与 nB 大小关 系 付费系数 k 实际付费 AA2 AB1 BA1 BB nA<nB 0 AA0 AB1 BA1 BB nA≥nB 2 k * Fi, j


由于最终所付费用与付费方式有关,所以 NS 中学的用户希望能够自行改变 自己的付费方式以减少总付费。然而,由于网络公司已经将每个用户注册时所选择的付费方式记录在案,所以对于用户 i,如果他/她想改变付费方式(由 A 改为 B 或由 B 改为 A) 就必须支付 Ci 元给网络公司以修改档案 , (修改付费方式记录)。 现在的问题是,给定每个用户注册时所选择的付费方式以及 Ci,试求这些用 户应该如何选择自己的付费方式以使得 NS 中学支付给网络公司的总费用最少 (更改付费方式费用+配对收费的费用)。【输入格式】 输入文件中第一行有一个正整数 N。 第二行有 2N 个整数,依次表示 1 号,2 号,?,2N 号用户注册时的付费方式, 每一个数字若为 0,则表示对应用户的初始付费方式为 A,否则该数字为 1,表 示付费方式为B。 第三行有 2N 个整数,表示每一个用户修改付费方式需要支付的费用,依次为 C1, C2, ⋯,CM 。( M=2N ) 以下2N-1 行描述给定的两两用户之间的流量表 F,总第(i +3)行第 j 列的整 数为 Fi, j+i 。(1≤i<2N,1≤j≤2N �C i) 所有变量的含义可以参见题目描述。【输出格式】 你的程序只需要向输出文件输出一个整数,表示 NS 中学支付给网络公司的 最小总费用。(单位:元)【输入样例】 2 1010 2 2 10 9 10 1 2 21 3 【输出样例】 8 【样例说明】 将 1 号用户的付费方式由 B 改为 A,NS 中学支付给网络公司的费用达到最小。 【评分方法】 本题没有部分分,你的程序的输出只有和我们的答案完全一致才能获得满 分,否则不得分。 【数据规模和约定】40%的数据中 N≤4; 80%的数据中 N≤7; 100%的数据中 N≤10,0≤Fi, j≤500,0≤Ci≤500 000。

F[I,j,k]表示在点 i 所管辖的所有用户中,有 j 个用户为 A,在 I 的每个祖先 u 上,如果 N[a]>N 则标 0 否则标1,用二进制状态压缩进 k 中,在这种情况下的


最小花费 F[I,j,k]:=min{f[l,u,k and(s<<(i-1))]+w1,f[r,j-u,k and(s<<(i1))]}
树形动态规划 12 千年虫

【问题描述】 千年虫是远古时代的生物,时隔几千万年,千年虫早已从地球上销声匿迹, 人们对其知之甚少。考古生物学家最近开始对其有了兴趣,因为一批珍贵的千年虫化石�环⑾郑�庑┗��A袅饲�瓿娼�跬暾�男翁�� 理论科学家们根据这些化石归纳出了千年虫的一般形态特征模型,并且据此判定出千年虫就是蜈蚣的祖先!但科学家 J 发现了实际与理论的一些出入,他仔 细的研究了上百个千年虫化石,发现其中大部分千年虫的形态都不完全符合理论模型,这到底是什么因素造成的呢?理论科学家 K 敏锐的指出,千年虫的形态 保存在化石中很有可能发生各种变化,即便最细微的变化也能导致它不符合模型。 于是,摆在科学家面前的新问题诞生了:判断一个化石中的千年虫与理论模 型的差距有多大?具体来说,就是根据一个千年虫化石的形态 A,找到一个符合 理论模型的形态 B,使 得 B 是最有可能在形成化石时变成形态 A。 理论学家提出的“千年虫形态特征模型” 如下(如右图所示):躯体由头、尾、躯干、 足四大部分构成。 1.头,尾用一对平行线段表示。称平行于 头、尾的方向为 x 方向;垂直于 x 的方向为 y 方向; 2.在头尾之间有两条互不相交的折线段相连,他们与头、尾两条线段一起围成的区域称 为躯干,两条折线段都满足以下条件:拐角 均为钝角或者平角,且包含奇数条线段,从 上往下数的奇数条垂直于 x 方向。 3.每条折线段从上往下数的第偶数条线段 的躯干的另一侧长出一条足,即一个上、下底平行于 x 方向的梯形或矩形,且其中远离躯干一侧的边垂直于 x 方向。注意:足不能退化成三角形(即底边的长度均大于零),躯干两侧足的数目 可以不一样。(如上图,左边有 4 条足,右边有 5 条足) 可见,x-y 直角坐标系内,躯干和所有足组成的实 心区域的边界均平行或垂直于坐标轴。为了方便,我们假设所有这些边界的长度均为正整数。因此可以认 为每个千年虫的躯体都由一些单位方格拼成。每个单 位方格都由坐标(x,y)唯一确定。设头尾之间的距离为 n,则我们可以用 2× 个整数来描述一条千年虫 B(如 n 右图):将 B 沿平行 x 轴方向剖分成 n 条宽度为 1 的横条,每个横条最左边一格的 x 坐标设为 Li,最右一格的的 x 坐标设为 Ri。则


(n,L1,L2,..,Ln,R1,R2,..Rn)就确定了一条千年虫。 由于岁月的侵蚀,在实际发现的化石中,千年虫的形状并不满足上面理论模 型的规则,一些格子中的躯体已经被某些矿物质溶解腐蚀了。地质、物理、生物学家共同研究得出: 1、腐蚀是以格子为单位的,只能一整格被腐蚀; 2、腐蚀是分步进行的,每一步只有一格被腐蚀; 3、如果去掉一个格子后躯体不连通了,那么这个格子当前不会被腐蚀; 4、如果一个格子的左边邻格和右边邻格都还没被腐蚀,那么这个格子当前 不会被腐蚀; 5、与头相邻的格子不能全部被腐蚀,与尾相邻的格子不能全部被腐蚀;倘若满足上面五条,我们仍然可以用(n,L’1,L’2,..,L’n,R’1,R’2,..R’n)来描述一个 化石里头的千年虫的形态。其中 L’i≤R’i。 例如下图: 现在你的任务是,输入一个化石里的千年虫的描述A<n,l’,r’></n,l’,r’>,找一个满足 理论模型的千年虫的描述 B,使得 B 可以通过腐蚀过程得以变为 A,且 由 B 转化为 A 的代价(须被腐蚀的格子数)最少。输出此最小代价。 【输入格式】 第一行为一个整数 n; 以下 n 行,每行两个整数,其中第 i 行为两个整数 L’i,R’i,用一个空格分开; 保证输入数据合法。 【输出格式】 仅一行,为一个整数,表示最少代价。 【输入样例】 7
4,4 3,4 3,5 1,3 2,2 2,4 3,3 1 2 3 4 5 1 2 3 4 5 L’,R’ 3,4 3,4 3,5 1,3 2,3 1,43,3 L,R

44 34 35 13


22 24 33 【输出样例】 3 【样例说明】 如右图 【数据规模和约定】 30%的数据 n≤100, 0≤L’i≤R’i≤100 50%的数据 n≤1000, 0≤L’i≤R’i≤1000 70%的数据 n≤100000,0≤L’i≤R’i≤1000 100%的数据 n≤1000000, 0≤L’i≤R’i≤1000000 【评分方法】本题没有部分分,你的程序的输出只有和我们的答案完全一致才能获得满 分,否则不得分。
12345

树形动态规划 13 IOI2005 河流

-----IOI2005 河流几乎整个 Byteland
口处有一个村庄——Bytetown。

王国都被森林和河流所覆盖。小点的河汇聚到一起,形成了

稍大点的河。就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进了大海。这条大河的入海 在Byteland 国,有 n 个伐木的村庄,这些村庄都座落在河边。目前在 Bytetown,有一个巨大的伐木 场,它处理着全国砍下的所有木料。木料被砍下后,顺着河流而被运到 Bytetown 的伐木场。Byteland 的 国王决定,为了减少运输木料的费用,再额外地建造 k 个伐木场。这 k 个伐木场将被建在其他村庄里。这 些伐木场建造后,木料就不用都被送到 Bytetown 了,它们可以在 运输过程中第一个碰到的新伐木场被处 理。显然,如果伐木场座落的那个村子就不用再付运送木料的费用了。它们可以直接被本村的伐木场处理。注:所有的河流都不会分叉,形成一棵树,根结点是 Bytetown。 国王的大臣计算出了每个村子每年要产多少木料,你的任务是决定在哪些村子建设伐木场能获得最小的运费。其中运费的计算方法为:每一吨木料每千米 1 分钱。 编一个程序: 1.从文件读入村子的个数,另外要建设的伐木场的数目,每年每个村子产的木料的块数以及河流的描述。 2.计算最小的运费并输出。 输入格式 Input Format 第一行包括两个数 n(2<=n<=100),k(1<=k<=50,且 k<=n)。n 为村庄数,k 为要建的伐木场的数 目。除了 Bytetown 外,每个村子依次被命名为 1,2,3⋯⋯n,Bytetown 被命名为 0。 接下来 n 行,每行 3 个整数: wi——每年 i 村子产的木料的块数。(0<=wi<=10000) vi——离 i 村子下游最近的村子。(即 i 村子的父结点)(0<=vi<=n) di——vi 到 i 的距离(千米)。(1<=di<=10000) 保证每年所有的木料流到 bytetown 的运费不超过 2000,000,000 分 50%的数据中 n 不超过 20。 输出格式Output Format


输出最小花费,精确到分。

树形动态规划 14 -----访问术馆

-----访问术馆

经过数月的精心准备,Peer Brelstet,一个出了名的盗画者,准备开始他的下一 个行动。艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向一个展览室。 Peer 知道每个展室 里藏画的数量,并且他精确测量了通过每条走廊的时间。由 于经验老到,他拿下一幅画需要 5 秒的时间。你的任务是编一个程序,计算在警 察赶来之前,他最多能偷 到多少幅画。

Input
第 1 行是警察赶到的时间,以 s 为单位。第 2 行描述了艺术馆的结构,是一串非 负整数,成对地出现:每一对的第一个数是走过一条走廊的时间,第 2 个数是它 末端的藏画数量; 如果第 2 个数是 0, 那么说明这条走廊分叉为两条另外的走廊。 数据按照深度优先的次序给出,请看样例。 一个展室最多有 20 幅画。通过每个走廊的时间不超过 20s。艺术馆最多有 100 个展室。警察赶到的时间在 10min 以内。

Output
输出偷到的画的数量。


Sample Input
gallery.in(如图 6-6) 60 7 0 8 0 31 14 2 10 0 12 4 6 2

Sample Output
gallery.out 2 f[i,j-c×2]:= max ( f[l,k], f[r,j-c×2-k] )

地图动态规划
地图动态规划 1 NOI 2005 adv19910

-----NOI 2005 adv19910 F[t,i,j]:=max{f[t-1,i-dx[d[[t]],j-dy[d[k]]]+1],f[t-1,i,j];

地图动态规划-2----优化的 NOI 2005adv19910

-----优化的 NOI 2005 adv19910 F[k,i,j]:=max{f[k-1,i,p]+1}j-b[k]<=p<=j;
地图动态规划 3 vijos 某题

-----vijos 某题F[I,j]:=min(f[i-1,j-1],f[I,j-1],f[i-1,j]);


目标动态规划
目标动态规划 1 CEOI98 subtra

-----CEOI98 subtra F[I,j]:=f[I-1,j+a] or f[i-1,j-a]

目标动态规划 2 Vijos 1037 搭建双塔问题

----- Vijos 1037 搭建双塔问题F[value,delta]:=g[value+a,delta+a] or g[value,delta-a]

概率动态规划
概率动态规划 1-----聪聪和可可(NOI2005)

-----聪聪和可可(NOI2005) x:=p[p[i,j],j]f[I,j]:=(f[x,b[j,k]]+f[x,j])/(l[j]+1)+1 f[I,i]=0 f[x,j]=1

概率动态规划 2 血缘关系

-----血缘关系 我们正在研究妖怪家族的血缘关系。每个妖怪都有相同数量的基因,但是不同的 妖怪的基因可能是不同的。我们希望知道任意给定的两个妖怪之间究竟有多少 相同的基因。由于基因数量相当庞大,直接检测是行不通的。但是,我们知道妖 怪家族的家谱,所以我们可以根据家谱来估算两个妖怪之间相同基因的 数量。


妖怪之间的基因继承关系相当简单:如果妖怪 C 是妖怪 A 和 B 的孩子,则 C 的任意一个基因只能是继承 A 或 B 的基因,继承 A 或 B 的概率各占 50%。所有 基因可认为是相互独立的,每个基因的继承关系不受别的基因影响。现在,我们来定义两个妖怪 X 和 Y 的基因相似程度。例如,有一个家族,这个家族中有两个毫无关系(没有相同基因)的妖怪 A 和 B,及它们的孩子 C 和 D。 那么 C 和 D 相似程度是多少呢?因为 C 和 D 的基因都来自 A 和 B,从概率来说, 各占 50%。所以,依概率计算 C 和 D 平均有 50%的相同基因,C 和 D 的基因相 似程度为 50%。需要注意的是,如果 A 和 B 之间存在相同基因的话,C 和 D 的基 因相似程度就不再是 50%了。 你的任务是写一个程序, 对于给定的家谱以及成对出现的妖怪,计算它们之间的基因相似程度

F[A, B]=(f[A0, B]+P[A1, B])/2 f[I,i]=1 f[I,j]=0(I,j 无相同基因)

其他类型
1 记忆化搜索

-----Vijos 某题,忘了F[pre,h,m]:=sigma{SDP(I,h+1,M+i)} (pre<=i<=M+1)

2 状态压缩动态规划

-----APIO 2007 动物园 APIO2007 《动物园 zoo》 题目简述:N 个动物围成一个环,有 K 个小朋友,每个小朋友可以看到五个 连续的动物,每个小朋友都有自己喜欢或讨厌的动物,当有一个自己讨厌 的动物被移走或能看到一个自己喜欢的动物时,这个小朋友就会高兴。求最优的移走动物的方案,使得最多的小朋友高兴。 总结:dp,又是 dp??还是状压 dp??郁闷了??没什么多说的,也不是


很复杂,主要要注意的就是因为是环,所以前 4 个点的状态必须一开始固定 死,以下是解题报告(来源:here is rj): 动态规划,用二进制表示动物的取舍。 F[i][j]表示从 1 到 i+4 的动物,其中 i 到 i+4 的动物的取舍状态为 j 时,最多 的高兴的小朋友的数目。 状态转移方程为:F[i][j]=max{F[i-1][(j<<1)&31],F[i-1][((j<<1)&31)|1]}+num[i][j];j 的二进制串中,最后一位表示 i,倒数第二为表示 i+1,以此类推。 num[i][j]为看到 i 到i+4 的动物的小朋友在 j 的状态下高兴的数目; 边界条件:F[0][j]=0;目标函数:max{F[N][j]}(0<=j<32); 不过,这是环形,因此要枚举固定开头的 4 个动物。 算法分析: 动规的阶段为 O(2^4),状态为 O(2^5*N),状态转移 O(1); 总的时间复杂度为 O(2^9*N); 基本可以承受。 type link=^node;node=record fr,lk:longint; next:link; end; var n,c,ans:longint;st:array[0..10001] of link; f:array[0..10001,0..31] of longint; procedure init;


begin assign(input,'zoo.in'); assign(output,'zoo.out'); reset(input);rewrite(output); end; procedure terminate; begin close(input); close(output);end; procedure add(e,sl,sf:longint); var p:link; beginnew(p);p^.fr:=sf;p^.lk:=sl;p^.next:=st[e];st[e]:=p; end; procedure readdata;var i,e,f,l,j,sf,sl,x,del:longint; p:link; begin fillchar(st,sizeof(st),0);readln(n,c); for i:=1 to c do begin read(e,f,l); del:=0; if e>=n-3 thendel:=n; sl:=0;sf:=0; for j:=1 to f do begin read(x); if x<e then x:=x+del;sf:=sf or (1 shl (x-e)); end; for j:=1 to l do begin read(x); if x<e thenx:=x+del; sl:=sl or (1 shl (x-e))


end; add(e,sl,sf); end; {for i:=1 to n do begin p:=st[i]; while p<>nil dobegin writeln(i,' ',binstr(p^.fr,5),' ',binstr(p^.lk,5)); p:=p^.next; end; end;} end; procedure prepare; var i,j:longint; begin for j:=0 to 31 do f[0,j]:=0;end; function calc(s,sta:longint):longint; var p:link; begin calc:=0; p:=st[s];while p<>nil do begin if sta and p^.lk>0 then inc(calc) else if sta andp^.fr<p^.fr then inc(calc); p:=p^.next; end; end; functionmax(a,b:longint):longint; begin if a>b then exit(a) else exit(b); end;procedure dp; var


i,s,j,x1,x2:longint; begin for s:=0 to 15 do begin for i:=1 to n do for j:=0 to31 do begin if i<=4 then begin x1:=j and (1 shl (5-i)-1); x2:=s shr (i-1);if x2<>x1 then begin f[i,j]:=0; continue; end; end else if i>=n-3 thenbegin x1:=j shr (n-i+1); x2:=s and (1 shl (4-n+i)-1); if x2<>x1 thenbegin f[i,j]:=0; continue; end; end; f[i,j]:=max(f[i-1,(j shl 1) and 31],f[i-1,(jshl 1) and 31 or 1])+calc(i,j); end; for j:=0 to 31 do if ans<f[n,j] thenans:=f[n,j]; end; end; procedure main; begin prepare; ans:=0; dp; writeln(ans);end;


begin init; readdata; main; terminate; end. f[I,k]:=f[i-1,k and not(1<<4)] + NewAddVal

3 字符串动态规划

-----Ural 1002 Phone if exist(copy(s,j,i-j)) then f:=min(f,f[j]+1);

4 多进程动态规划

-----CEOI 2005 service Min( f[i,j,k], f[i-1,j,k] + c[t[i-1],t] ) Min(f[i,t[i-1],k], f[i-1,j,k] + c[j,t] ) Min( f[i,j,t[i-1]], f[i-1,j,k] + c[k,t] )

5 多进程动态规划

-----Vijos1143 三取方格数max(f[i,j,k,l],f[i-1,j-R[m,1],k-R[m,2],l-R[m,3]]); if (j=k) and (k=l) theninc(f[i,j,k,l],a[j,i-j]) else if (j=k) then inc(f[i,j,k,l],a[j,i-j]+a[l,i-l])else if (k=l) then inc(f[i,j,k,l],a[j,i-j]+a[k,i-k]) else if (j=l) theninc(f[i,j,k,l],a[j,i-j]+a[k,i-k]) elseinc(f[i,j,k,l],a[j,i-j]+a[k,i-k]+a[l,i-l]);

6 多进程动态规划

-----巡游加拿大(IOI95、USACO) d[i,j]=max{d[k,j]+1(a[k,i] & j<k<i),d[j,k]+1(a[I,j] &(k<j))}。 f[i,j]表示从起点出发,一个人到达i,另一个人到达 j 时经过的城市数。d[i,j]=d[j,i],所以我们限制 i>j 分析状态(i,j),它可能是(k,j)(j<k<i)中 k 到达 i 得到(方式 1),也可能是(j,k)(k<j)中 k 超过 j 到达 i 得到(方式 2)。但它不能是(i,k)(k<j)中 k 到 达j 得到,因为这样可能会出现重复路径。即使不会出现重复路径,那么它


由(j,k)通过方式 2 同样可以得到,所以不会遗漏解时间复杂度 O(n3)

7 括号序列

-----线型动态规划f[I,j]:=min(f[I,j],f[i+1,j-1](ss[j]=”()”or(”[]”)), f[I+1,j+1]+1 (s[j]=”(”or”[”] , f[I,j-1]+1(s[j]=”)”or”]”

)

8 棋盘切割

-----线型动态规划f[k,x1,y1,x2,y2]=min{min{f[k-1,x1,y1,a,y2]+s[a+1,y1,x2,y2],f[k-1,a+1,y1,x2,y2]+s[x1,y1,a,y2] min{}}
一、问题描述 将一个8×8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩 形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有 n 块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)

允许的分割方案

不允许的分割方案


原棋盘上每一格有一个分值, 一块矩形棋盘的总分为其所含各格分值之和。 现在需要把 棋盘按上述规则分割成 n 块矩形棋盘,并使各矩形棋盘总分的均方差最小。

均方差

,其中平均值

,xi 为第 i 块矩形棋盘的总分。

请编程对给出的棋盘及 n,求出 的最小值。 输入 第 1 行为一个整数 n(1<n<15)。 第 2 行至第 9 行每行为 8 个小于 100 的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。 输出 仅一个数,为 (四舍五入精确到小数点后三位) 。 样例输入 3 1111111311111111 11111111 11111111 11111111 11111111 11111110 11111103 样例输出 1.633 二、初步分析 本题的实质是求一种最优的棋盘分割方式,使得每块棋盘与平均值的差值之和最小。 首先我们必须明确几个条件(关键字),这样对我们理解题意进而采取正确的算法解决 问题大有帮助:

?

均方差

:在实际编程的过程中,由于 n 是定值,实际只需求

(Xi-X)的和的值作为参数,以此简化程序的计算及存储的空间。? 本题提供固定的8×8棋盘,并不算大,这是本题有解的重要条件,并且给我们一 个暗示:以棋盘格子为存储单位,将有很大的自由度。

于是我们开始考虑算法: 对比八皇后问题的复杂度, 我们不难看出这道题需要搜索更多 的内容,在时间上搜索算法实不可取;因此,只能使用动态规划实现本题。经过分析,不难


发现本题符合最优化原理:即若第 i 次分割为最佳分割,则第i-1 次分割为且必为最佳;定 义函数 F[i,j][i’,j’]为[i,j]、[i’,j’]分别为左上、右下角的棋盘的最优值,F0[i,j][i’,j’]为[i,j]、[i’,j’] 分别为左上、右下角的棋盘值,探寻函数 F[i,j][i’,j’]的动态转移方程。下面分析分割方式。当我们进行第 i 次分割时不外乎以下四种方式:

逐一进行分析: (图 3.4-3) 1. 横割方式: a) 第 i 次切割将棋盘分为上i-1 个棋盘和下 1 个棋盘(图(a) ) A1=F0[i1,j1][i’,j2]+F[i’+1,j1][i2,j2] b) 第i 次切割将棋盘分为下 i-1 个棋盘和上 1 个棋盘(图(b) ) A2=F[i1,j1][i’,j2]+F0[i’+1,j1][i2,j2]2. 竖割方式: c) 第 i 次切割将棋盘分为右 i-1 个棋盘和左 1 个棋盘(图(c)) A3=F[i1,j1][i2,j’]+F0[i1,j’+1][i2,j2] d) 第 i 次切割将棋盘分为左 i-1 个棋盘和右 1 个棋盘(图(d) ) A3=F0[i1,j1][i2,j’]+F [i1,j’+1][i2,j2] 状态转移方程为F[i1,j1][i2,j2]=min{A1,A2,A3,A4} (1<=i1,j1<=8,i1<=i2<=8,j1<=j2<=8,2<=k<=n)其中 k 代表分割的棋盘数,单调递增,因此第 k 次分割只与 k-1 次的结果有关,所以每 做完第 k 次对棋盘的规划 F0?F。由此节省下许多空间。 三、程序实现 下面我们讨论程序实现的具体步骤与代码的优化。 首先在读入过程段我们进行准备工作,累加计算出 F0 并统计出棋盘每个格子值之和 S 来计算平均数 Average。 s?0; for i:=1 to 8 do for j:=1 to8 do begin read(f[i,j][i,j]); s?s+f[i,j][i,j]; {读入棋盘每个格子的值,并统计其和} for i1:=1 to i do {枚举左上方坐标 i1,j1} forj1:=1 to j do for i2:=i to 8 do


for j2:=j to 8 do {枚举右上方坐标 i2,j2} if (i1<>i) or(j1<>j) or (i2<>i) or (j2<>j) thef[i1,j1][i2,j2]?f[i1,j1][i2,j2]+f[i,j][i,j]; end; 在套用循环算出 F0[i1,j1][i2,j2]的值,此处不再赘述。 然后用动态规划求解: fori:=2 to n do begin {分阶段,第 i 次分割}for i1:=1 to 8 do for j1:=1 to 8 do for i2:=i1 to 8 do for j2:=j1 to 8 do begin{确定坐上、右下角坐标} F[i1,j1][i2,j2]?max; for i’:=i1 to i2-1 dobegin 计算 A1,A2;F[i1,j1][i2,j2]?min{A1,A2}; end; for i’:=i1 to i2-1 do begin 计算 A3,A4;F[i1,j1][i2,j2]?min{F[i1,j1][i2,j2],A3,A4}; end; end; F 0?F; end; 显然问题的解为三、小结 本题是极有代表性的动态规划题型,较之 NOI99 的其他题目算是比较简单的。此题的 思路简单而明了,没有太多限制条件让人梳理不清,空间的自由度很大,唯一的限制便是运行时间。 所谓窥一斑可见全豹, 从本题的思考过程中, 我们不难总结出应用动态规划算法的一般 思路及步骤: ? 确定算法,整体估计可行度。一般从两方面着手:时空复杂度和最优化原理。 ? 建立模型,考虑可能出现的情况。 ? 建立动态转移方程。 ? 程序实现及代码优化。

9 最短路 1

-----Floyd f[i,j]:=max(f[i,j],f[i,k]+f[k,j]);


ans[q[i,j,k]]:=ans[q[i,j,k]]+s[i,q[i,j,k]]*s[q[i,j,k],j]/s[i,j];
10 双重动态规划

-----有限的基因序列 f:=min{f[j]+1} g[c,i,j]:=(g[a,i,j] andg[b,i,j]) or (g[c,i,j])

11 动态规划

-----ZOJ cheesef[i,j]:=f[i-kk*zl[u,1],j-kk*zl[u,2]]+a[i-kk*zl[u,1],j-kk*zl[u,2]]

12 动态规划

-----NOI 2004 berry 线性 F[I,1]:=sF[I,j]:=max{min{s-s[l-1]},f[l-1,j-1]} (2≤j≤k, j≤l≤i)

13 动态规划

-----NOI 2004 berry 完全无向图 F[I,j]:=f[i-1,j] or (j≥w) and(f[i-1,j-w])

14 动态规划

-----石子合并 四边形不等式优化 m[i,j]=max{m[i+1,j],m[i,j-1]}+t[i,j]

15 动态规划

-----CEOI 2005 service (k≥long,i≥1)g[i, j, k]=max{g[i-1,j,k-long]+1,g[i-1,j,k]}(k<long,i≥1) g[i, j, k]=max{g[i-1,j-1,t-long]+1,g[i-1,j,k]}(0≤j≤m, 0≤k<t) g[0,j,k]=0; ans:=g[n,m,0]。 状态优化:g[i, j]=min{g[i-1,j],g[i-1,j-1]+long} 其中(a, b)+long=(a’, b’)的计算方法为: 当 b+long ≤t 时: a’=a; b’=b+long; 当 b+long >t 时: a’=a+1; b’=long; 规划的边界条件: 当 0≤i≤n 时,g[i,0]=(0,0)


16 动态规划

-----AHOI 2006 宝库通道 f[k]:=max{f[k-1]+x[k,j]-x[k,i-1],x[k,j]-x[k,i-1]}

17 动态规划

-----Travel A) 费用最少的旅行计划。 设 f 表示从起点到第 i 个旅店住宿一天的最小费用;g 表示从起点到第 i 个旅 店住宿一天,在满足最小费用的前提下所需要的最少天数。那么:f=f[x]+v, g=g[x]+1 x 满足: 1、x<i,且 d �C d[x] <= 800(一天的最大行程)。 2、 对于所有的 t < i, d �C d[t]<= 800,都必须满足: A. g[x] < g[t](f[x] = f[t]时) B. f[x] < f[t] (其他情况) f[0] = 0,g[0] = 0。 Ans:=f[n + 1],g[n+1]。 B). 天数最少的旅行计划。 方法其实和第一问十分类似。 设 g’表示从起点到第 i 个旅店住宿一天的最少天数;f’表示从起点到第 i 个旅店住宿一天,在满足最小天数前提下所需要的最少费用。那么: g’ = g’[x] + 1, f’ = f’[x] + v x 满足: 1、 x<i,且 d �C d[x] <= 800(一天的最大行程)。 2、 对于所有的 t < i, d �C d[t]<= 800,都必须满足: f’[x] < f’[t] g’[x] = g’[t]时 g’[x] < g’[t] 其他情况 f’[0] = 0,g’[0] = 0。 Ans:=f’[n + 1],g’[n+1]。

18 动态规划

-----NOI 2007 cash y:=f[j]/(a[j]*c[j]+b[j]); g:=c[j]*y*a+y*b; f:=max(f,g)


19 描述 Description
永恒の灵魂最近得到了面积为 n*m 的一大块土地(高兴ING^_^),他想在这块土地上建造一所房子, 这个房子必须是正方形的。但是,这块土地并非十全十美,上面有很多不平坦的地方(也可以叫瑕疵)。 这些瑕疵十分恶心,以至于根本不能在上面盖一砖一瓦。他希望找到一块最大的正方形无瑕疵土地来盖房子。 不过,这并不是什么难题,永恒の灵魂在 10 分钟内就轻松解决了这个问题。现在,您也来试试吧。 输入格式 Input Format 输入文件第一行为两个整数 n,m(1<=n,m<=1000),接下来 n 行,每行 m 个数字,用空格隔开。0 表 示该块土地有瑕疵,1 表示该块土地完好。 输出格式 Output Format 一个整数,最大正方形的边长。样例输入 Sample Input 44 0111 1110 0110 1101 样例输出 Sample Output 2

20、描述 Description
有一个箱子容量为 v(正整数,o≤v≤20000),同时有 n 个物品(o≤n≤30),每个物品有一个体积 (正整 数)。要求从 n 个物品中,任取若千个装入箱内,使箱子的剩余空间为最小。输入格式 Input Format 第一行,一个整数,表示箱子容量; 第二行,一个整数,表示有 n 个物品; 接下来 n 行,分别表示这 n 个物品的各自体积。 输出格式 Output Format 一个整数,表示箱子剩余空间。样例输入 Sample Input 24 6 8 3 12 7 9 7 样例输出 Sample Output 0

内容来自:文档资料库http://www.03964.com/
更多"动态规划100例"相关资料请点击这里

  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值