区间合并类动态规划训练

来源:互联网 发布:红黑树java实现 编辑:程序博客网 时间:2024/06/05 08:39
啦啦又来发题解了~
今天我们练习了区间合并型的动态规划(一股小学生记叙文的气息扑面而来~
于是三个小时三道题~
还算不错?
1. 数字游戏(dgame)
【题目描述】丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易。游戏是这样的,在你面前有一圈整数(一共n个),你要按顺序将其分为m个部分,各部分内的数字相加,相加所得的m个结果对10取模后再相乘,最终得到一个数k。游戏的要求是使你所得的k最大或者最小。
这个很正常的区间合并
先预处理tot[i,j]表示从i到j总共和
f[i,j,p]表示从i开始j个数(包括j)分成k块最大值(最小值同理
f[i,j,p]:=max(f[i,j,p],f[i,k,p-1]*tot[i+k,i+j-1]);
这样就可以AC啦

代码


const shuru='dgame.in';      shuchu='dgame.out';var   a:array[0..100] of longint;      f,g:array[0..100,0..100,0..10] of longint;      tot:array[0..100,0..100] of longint;      ans,n,m,i,j,k,p:longint;function max(a,b:longint):longint;beginif a>b then exit(a);exit(b);end;function min(a,b:longint):longint;beginif a<b then exit(a);exit(b);end;procedure init;beginassign(input,shuru);assign(output,shuchu);reset(input);rewrite(output);readln(n,m);for i:=1 to n doreadln(a[i]);for i:=n+1 to (n shl 1) doa[i]:=a[i-n];for i:=1 to (n shl 1) dofor j:=i to (n shl 1) dobeginfor k:=i to j dotot[i,j]:=tot[i,j]+a[k];tot[i,j]:=tot[i,j] mod 10;while tot[i,j]<0 dotot[i,j]:=tot[i,j]+10;end;    dec(m);close(input);    for i:=1 to 100 do        for j:=1 to 100 do            for k:=1 to 10 do                g[i,j,k]:=maxlongint;for i:=1 to n dofor j:=1 to n do            begin       f[i,j,0]:=tot[i,i+j-1];               g[i,j,0]:=tot[i,i+j-1];            end;end;procedure main;begininit;for p:=1 to m dofor j:=1 to n do            if j>=p+1 thenfor i:=1 to n dofor k:=p to j-1 dog[i,j,p]:=min(g[i,j,p],g[i,k,p-1]*tot[i+k,i+j-1]);ans:=maxlongint;for i:=1 to n doans:=min(g[i,n,m],ans);writeln(ans);ans:=0;for p:=1 to m dofor j:=1 to n do            if j>=p+1 thenfor i:=1 to n dofor k:=p to j-1 dof[i,j,p]:=max(f[i,j,p],f[i,k,p-1]*tot[i+k,i+j-1]);ans:=0;for i:=1 to n doans:=max(f[i,n,m],ans);writeln(ans);close(output);end;beginmain;end.

 凸多边形的三角剖分(division)
【题目描述】给定一具有N个顶点(从1到N编号)的凸多边形,每个顶点的权均已知。问如何把这个凸多边形划分成N-2个互不相交的三角形,使得这些三角形顶点的权的乘积之和最小?
【输入格式】第一行 顶点数N(N<50)。第二行 N个顶点(从1到N)的权值,权值为小于32768的整数。
【输出格式】第一行为各三角形顶点的权的乘积之和最小值。

看到这题的时候愣了一下

但想到都是区间型的动态规划,于是就套公式

设f[i,j]是从i开始J个顶点可以得到的最小值
于是f[i,j]:=min(f[i,j],f[i,k]+f[(i+k-2) mod n+1,j-k+1]+a[i]*a[(i+k-2) mod n+1]*a[(i+j-2) mod n+1]);
然后就AC啦~
代码:


const shuru='division.in';      shuchu='division.out';var   f:array[0..200,0..200] of int64;      i,j,k,n:longint;      ans:int64;      a:array[0..100] of int64;procedure init;beginassign(input,shuru);assign(output,shuchu);reset(input);rewrite(output);readln(n);for i:=1 to n doread(a[i]);close(input);for i:=n+1 to (n shl 1) doa[i]:=a[i-n];for i:=1 to 200 dofor j:=1 to 200 dof[i,j]:=4000000000000000000;for i:=1 to 2*n do    begin        f[i,2]:=0;f[i,3]:=a[i]*a[i+1]*a[i+2];    end;end;function min(a,b:int64):int64;beginif a<b then exit(a);exit(b);end;procedure main;begininit;for j:=4 to n dofor i:=1 to n dofor k:=2 to j-1 dof[i,j]:=min(f[i,j],f[i,k]+f[(i+k-2) mod n+1,j-k+1]+a[i]*a[(i+k-2) mod n+1]*a[(i+j-2) mod n+1]);ans:=4000000000000000000;for i:=1 to n doans:=min(ans,f[i,n]);writeln(ans);close(output);end;beginmain;end.


3.青蛙的烦恼(frog)
【题目描述】池塘中有n片荷叶恰好围成了一个凸多边形,有一只小青蛙恰好站在1号荷叶上,小青蛙想通过最短的路程遍历所有的荷叶(经过一个荷叶一次且仅一次),小青蛙可以从一片荷叶上跳到另外任意一片荷叶上。
【输入格式】第一行为整数n,荷叶的数量。接下来n行,每行两个实数,为n个多边形的顶点坐标,按照顺时针方向给出。保证不会爆double。【输出格式】遍历所有荷叶最短路程,请保留3位小数。
乍一看是货郎担呢> <(其实它就是个货郎担
但是我们知道货郎担是2^n次级别的,可是这里的n有720(你想试试2^720的滋味吗我的评测机?
所以肯定有一些特别
注意到它是个凸多边形
首先无论你是凸多边形还是凹多边形你肯定不能交叉(证明略
但是凸多边形上不能交叉就意味着你从一个点出发后只能向它相邻的两个点走去
因为如果你不这样的话你回头来走相邻的这个点就会和原来的路线交叉
我们把这些荷叶按顺时针从1..n编号
可以知道无论如何剩下的没有访问的荷叶都是顺序的,即i..j
设f[i,j,0]表示从i到j都没有访问,而且青蛙在i上(注意这时候i还算没访问),而我们要把剩下的i..j都访问完所需的最短值
设f[i,j,1]表示从i到j都没有访问,而且青蛙在j上(注意这时候j还算没访问),而我们要把剩下的i..j都访问完所需的最短值
可得
f[i,j,0]:=min(f[i+1,j,0]+d[i,i+1],f[i+1,j,1]+d[i,j]);
f[i,j,1]:=min(f[i,j-1,1]+d[j,j-1],f[i,j-1,0]+d[i,j]);
然后就AC辣~
代码:

const shuru='frog.in';      shuchu='frog.out';type  pppp=recordx,y:double;   end;var   f:array[0..1000,0..1000,0..1] of double;      d:array[0..1000,0..1000] of double;      a:array[0..1000] of pppp;      i,j,k,n:longint;function dist(a,b:pppp):double;beginexit(sqrt(sqr(a.x-b.x)+sqr(a.y-b.y)));end;function min(a,b:double):double;beginif a<b then exit(a);exit(b);end;procedure init;beginassign(input,shuru);assign(output,shuchu);reset(input);rewrite(output);readln(n);for i:=1 to n dowith a[i] do readln(x,y);close(input);for i:=1 to n dofor j:=1 to n dod[i,j]:=dist(a[i],a[j]);end;procedure main;begininit;for i:=n downto 1 dofor j:=i to n dobeginf[i,j,0]:=min(f[i+1,j,0]+d[i,i+1],f[i+1,j,1]+d[i,j]);f[i,j,1]:=min(f[i,j-1,1]+d[j,j-1],f[i,j-1,0]+d[i,j]);end;write(f[1,n,0]:0:3);close(output);end;beginmain;end.

最后一题相比第二题第三题就显得很水很水的了
唉算了不想讲了
直接贴代码


const shuru='stones.in';  shuchu='stones.out';vara:array[0..101] of longint;step,f,g:array[0..201,0..201] of longint;ans,i,j,k,n:longint;function max(a,b:longint):longint;beginif a>b then exit(a);exit(b);end;function min(a,b:longint):longint;beginif a<b then exit(a);exit(b);end;procedure init;beginassign(input,shuru);assign(output,shuchu);reset(input);rewrite(output);readln(n);for i:=1 to n doread(a[i]);close(input);    for i:=1 to n do        for j:=1 to n do            g[i,j]:=maxlongint;for i:=1 to n dobeginf[i,1]:=0;g[i,1]:=0;end;for i:=n+1 to 2*n doa[i]:=a[i-n];for i:=1 to 2*n dofor j:=i to 2*n dofor k:=i to j dostep[i,j]:=step[i,j]+a[k];end;procedure main;begininit;for j:=2 to n dofor i:=1 to n dofor k:=1 to j-1 dog[i,j]:=min(g[i,j],g[i,k]+g[(i+k-1) mod n+1,j-k]+step[i,i+k-1]+step[i+k,i+j-1]);    ans:=maxlongint;    for i:=1 to n doans:=min(ans,g[i,n]);writeln(ans);ans:=0;for j:=2 to n dofor i:=1 to n dofor k:=1 to j-1 dof[i,j]:=max(f[i,j],f[i,k]+f[(i+k-1) mod n+1,j-k]+step[i,i+k-1]+step[i+k,i+j-1]);for i:=1 to n doans:=max(ans,f[i,n]);writeln(ans);close(output);end;beginmain;end.



好了题目说完了来说说自己的感悟吧
区间合并型(即环形)通常设f[i,j]为从i到j合并起来的最值
如果要分开成多个部分的话多加一维即可 那一维表示分成的块数
这样就可以很愉快的AC了呢 > <


By WJZ
2014.8.6 16:35
(暑假过的好快啊

0 0