动规重要入门例题.docx
- 文档编号:3614309
- 上传时间:2022-11-24
- 格式:DOCX
- 页数:13
- 大小:39.42KB
动规重要入门例题.docx
《动规重要入门例题.docx》由会员分享,可在线阅读,更多相关《动规重要入门例题.docx(13页珍藏版)》请在冰豆网上搜索。
动规重要入门例题
第1题机器分配
(文件名:
jqfp.pas/exe)
【问题描述】
总公司拥有高效生产设备M台,准备分给下属的N个分公司。
各分公司若获得这些设备,可以为国家提供一定的盈利。
问:
如何分配这M台设备才能使国家得到的盈利最大?
求出最大盈利值。
其中M<=15,N<=10。
分配原则:
每个公司有权获得任意数目的设备,但总台数不得超过总设备数M。
【数据输入】jqfp.in
输入文件第一行是两个数,第一个数是设备台数M,第二个数是分公司数N。
第二行开始是一个M*N的矩阵,分别表示机器台数分配给不同公司的不同盈利。
【数据输出】jqfp.out
输出文件第一行是一个数,即最大盈利值;
第二行开始共N行,每行2个数,第一个数是公司编号(按升序),第二个表示该公司应该分配的机器数目。
公司编号
机器台数盈利
1
2
3
0
0
0
0
1
3
5
4
2
7
11
6
3
9
11
11
4
12
11
12
5
13
11
12
【样例输入】jqfp.in
53
0000
1354
27116
391111
4121112
5131112
【样例输出】jqfp.out
22
10
22
33
(机器数量分配有可能不同,但满足最大盈利,所以算正确)
【分析】
用机器数来做状态,数组F[I,J]表示前I个公司分配J台机器的最大盈利。
则状态转移方程为:
F[I,J]=Max{F[I-1,K]+Value[I,J-K]}(1<=I<=N,1<=J<=M,0<=K<=J)
初始值:
F(0,0)=0
时间复杂度O(N*M2)
算法描述:
S1:
读入数据
S2:
动态规划求解:
fori:
=1tondo{阶段,给前i个公司分配机器}
forj:
=0tomdo{状态,分配给这i个公司的机器台数}
fork:
=0tojdo{决策,给前(i-1)个公司分配k台机器,
并给i号公司分配(j-k)台机器}
ifb[i-1,k]+a[i,j-k]>b[i,j]then
b[i,j]:
=b[i-1,k]+a[i,j-k];
{当前的方案更优then更新b[i,j]的值}
S3:
倒推求出每步的选择方法并输出最有方案。
【参考程序】
PROGRAMjqfp;
const
fi='jqfp.in';
fo='jqfp.out';
var
M,N:
byte;
mem:
array[0..10,0..15]ofinteger;
knl:
array[0..10,0..15]oflongint;
rcv:
array[0..10,0..15,1..15]ofinteger;
PROCEDUREInit;
var
i,j:
byte;
begin
fillchar(mem,sizeof(mem),0);
fillchar(knl,sizeof(knl),0);
fillchar(rcv,sizeof(rcv),0);
assign(input,fi);
reset(input);
readln(M,N);
fori:
=0toMdo
begin
read(j);
forj:
=1toNdo
read(mem[j,i]);
end;
close(input);
end;
PROCEDUREKernel;
var
i,j,k,l:
byte;
begin
fori:
=1toNdo
forj:
=0toMdo
fork:
=0tojdo
ifknl[i-1,k]+mem[i,j-k]>knl[i,j]then
begin
knl[i,j]:
=knl[i-1,k]+mem[i,j-k];
forl:
=1toi-1do
rcv[i,j,l]:
=rcv[i-1,k,l];
rcv[i,j,i]:
=j-k;
end;
end;
PROCEDUREPrint;
var
i:
byte;
begin
assign(output,fo);
rewrite(output);
writeln(knl[N,M]);
fori:
=1toNdo
writeln(i,'',rcv[N,M,i]);
close(output);
end;
begin
Init;
Kernel;
Print;
end.
第2题最长不下降序列
(文件名:
bxjxl.pas/exe)
【题意描述】
设有整数序列b1,b2,b3,…,bm,若存在下标i1 求序列b1,b2,b3,…,bm中所有长度(n)最大不下降子序列 【数据输入】bxjxl.in 一行整数序列(长度≤10000),其中每个数字可以多次用在不同系列中。 【数据输出】bxjxl.out 只有一行,输出两个数,即最大长度n和所有长度为n的序列个数total(如果有相同的系列,算1个) 【样例输入】bxjxl.in 456126 【样例输出】bxjxl.out 32 即有以下2个长度为3的序列(下面内容不输出) 126 456 【分析】 (1)设f(i)为前i个数中的最大不下降序列长度,则 f(i)=max{f(j)+1}(1<=j 边界为F (1)=1 (2)设t(i)为前i个数中最长不下降序列的个数,则 t(i)=∑t(j)(1<=j 初始为t(i)=1 当f(i)=n时,将t(i)累加 举例: 1234658109 f: 123455677 t: 111111222 答案: f=7时,边界为∑t=4 (3)求本质不同的最长不下降序列个数有多少个? 如: 1234658109有, 12346810,12345810,1234689,1234589 都是本质不同的。 但对于 1223354 f1223354 t1112244 答案有8个,其中4个1235,4个1234 改进算法: 上例显然对于相两个相同的数,重复算了多次因此,我们对算法进行改进: 对原序列按b从小到大(当bi=bj时按F从大到小)排序,增设Order(i)记录新序列中的i个数在原序列中的位置。 可见, 求t(i)时,当f(j)=f(j+1),b(j)=b(j+1)且Order(j+1) 这样就避免了重复。 上述算法的时间复杂度为O(n2) 【参考程序】 programevil(input,output); const finp='bxjxl.in'; foup='bxjxl.out'; maxn=1000; var n,len: longint; tot: longint; f,b: array[1..maxn]oflongint; procedureinit; var f: text; begin assign(f,finp); reset(f); n: =0; whilenot(eoln(f))do begin inc(n); read(f,b[n]); end; close(f); end; proceduremain; var i,j,t: longint; order: array[1..maxn]oflongint; total: array[1..maxn]oflongint; begin f[1]: =1;len: =1; fori: =2tondo begin f[i]: =1; forj: =1toi-1do if(b[i]>b[j])and(f[i] thenf[i]: =f[j]+1; iff[i]>lenthenlen: =f[i]; end; fori: =1tondo order[i]: =i; fori: =1tondo forj: =i+1tondo if(b[i]>b[j])or(b[i]=b[j])and(f[i]>f[j]) thenbegin t: =b[i]; b[i]: =b[j]; b[j]: =t; t: =f[i]; f[i]: =f[j]; f[j]: =t; t: =order[i]; order[i]: =order[j]; order[j]: =t; end; tot: =0; fillchar(total,sizeof(total),0); fori: =1tondo begin iff[i]=1thentotal[i]: =1 else forj: =i-1downto1do if(f[j]=f[i]-1)and(b[j] if(b[j+1]<>b[j])or(f[j+1]<>f[j])or(order[j+1]>=order[i]) theninc(total[i],total[j]); if(f[i]=len)and(b[i]<>b[i+1])then tot: =tot+total[i] end; end; procedureout; begin assign(output,foup); rewrite(output); write(len,''); writeln(tot); close(output); end; begin init; main; out; end. 第3题凸多边形三角划分 (文件名: triangle.pas/exe) 【题意描述】 给定一个具有N(N<50)个顶点(从1到N编号)的凸多边形,每个顶点的权均已知。 问如何把这个凸多边形划分成N-2个互不相交的三角形,使得这些三角形顶点的权的乘积之和最小? 【数据输入】triangle.in 第一行,输入顶点数N 第二行,输入N个顶点(从1到N)的权值 【数据输出】triangle.out 第一行,输出最小的和的值 第二行,输出各三角形组成的方式(顶点编号表示),相邻三角形之间用“,”隔开。 (顺序可能不同) 【输入样例】triangle.in 5 121122123245231 【输出样例】triangle.out 12214884 345,153,123(顺序可能不同) 【分析】 本题是一道典型的动态规划问题(三角剖分问题一般用动态规划解答)。 用一个二维状态F(I,J)(I 于是,目标状态就可以表示为F(1,N)。 有了状态描述,注意到边(I,J)总是要被包含于某个三角形中(三角剖分的特点),假若他们与定点K共同形成一个三角形(I,J,K),据此写出下面的动态转移方程: F[I,J]=Min{F[I,K]+F[K,J]+S[I]*S[J]*S[K]}(0 初始条件: F[1,2]=0,目标状态: F[1,N]。 阶段可以按照区间的大小来划分,边界状态定为退化的线段,即F(I,I+1)=0,中间有关数的精度要加以注意,我们可以发现,由于这里为乘积之和,在输入数据较大时有可能超过长整形范围,所以还需用高精度计算。 本题虽然简单,但也有一些值得思考的地方没,并且,以三角形剖分为原型繁衍了大量的名题佳题,需要就一步研究。 第4题石子合并 (文件名: unite.pas/exe) 【题意描述】 如图1所示,在一个园形操场的四周摆放N堆石子(N≤100),现要将石子有次序地合并成一堆。 规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。 编一程序,由文件读入堆数N及每堆的石子数(≤20), 图1 ①选择一种合并石子的方案,使得做N-1次合并,得分的总和最小; ②选择一种合并石子的方案,使得做N-1次合并,得分的总和最大。 例如,图1所示的4堆石子,每堆石子数(从最上面的一堆数起,顺时针数)依次为4594。 则3次合并得分总和最小的方案为图2,得分总和最大的方案为图3。 【数据输入】 输出文件名为unite.in,该文件内容为: 第一行为石子堆数N; 第二行为每堆的石子数,每两个数之间用一个空格符分隔。 【数据输出】 输出文件名为unite.out 从第1至第N行为得分最小的合并方案。 第N+1行是空行。 从第N+2行到第2N+1行是得分最大合并方案。 每种合并方案用N行表示,其中第i行(1≤i≤N)表示第i次合并前各堆的石子数(依顺时针次序输出,哪一堆先输出均可)。 要求将待合并的两堆石子数以相应的负数表示,以便标识。 【输入样例】 4 4594 【输出样例】 -459-4 -8–59 -9–13 -22 4–5–94 4–14–4 -4–18 -22 【分析】 看到本题,容易想到贪心法,即每次选取相邻最大或最小的两堆石子合并。 然而这样做对不对呢? 看一个例子。 N=5,石子数分别为346542。 用贪心法的合并过程如下: 第一次346542得分5 第二次54654得分9 第三次9654得分9 第四次969得分15 第五次159得分24 第六次24 总分: 62 然而仔细琢磨后,发现更好的方案: 第一次346542得分7 第二次76542得分13 第三次13542得分6 第四次1356得分11 第五次1311得分24 第六次24 总分: 61 显然,本题采用贪心法是错误的,贪心法导致局部最优不一定导致全局最优。 动态规划: 用data[i,j]表示将从第i颗石子开始到第j颗石子合并所得的分值和,即 data[i,j]= {xk为第k个石子的分值}。 max[i,j]表示将从第i颗石子开始的接下来j颗石子合并可能的最大值,那么: max[i,j]=max{max[i,k]+max[i+k,j–k]+data[i,k]+data[i+k,j–k]} (2<=k<=j) 初始值max[i,1]=0。 同理,我们用min[i,j]表示将第从第i颗石子开始的接下来j颗石子合并所得的最小值,可以得到类似的方程: min[i,j]=min{min[i,k]+min[i+k,j–k]+data[i,k]+data[i+k,j–k]} (0<=k<=j) 初始值min[i,0]=0 这样,我们完美地解决了这道题。 时间复杂度也是O(n2)。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 重要 入门 例题
![提示](https://static.bdocx.com/images/bang_tan.gif)