DAG上的动态规划:
有向无环图上的动态规划是学习DP的基础,很多问题都可以转化为DAG上的最长路、最短路或路径计数问题。
1.没有明确固定起点重点的DAG模型:
嵌套矩形问题:有n个矩形,每个矩形可以用两个整数a、b表示它的长和宽,矩形可以嵌套在矩形中当且仅当a<c,b<d或者b<c,a<d。选出尽量多的矩形排成一行,使得除了最后一个之外,每个矩形都可以嵌套在下一个矩形内。如果有多解矩形编号字典序应尽量小。
1 /**
2 * 嵌套矩形问题:有n个矩形,每个矩形可以用两个整数a、b表示它的长和宽,
3 * 矩形可以嵌套在矩形中当且仅当a<c,b<d或者b<c,a<d。选出尽量多的矩形排成一行,使得除了最后一个之外,每个矩形都可以嵌套在下一个矩形内。如果有多解矩形编号字典序应尽量小。
4 */
5 static int[] d= {-1,-1,-1,-1,-1,-1};
6 static int[][]a=new int[6][6];
7
8 public static void getA(ErYuan[] es) {//建立单向无环图
9 for(int i=0;i<es.length;i++) {
10 for(int j=0;j<es.length;j++) {
11 if(es[i].isOk(es[j])) {
12 a[i][j]=1;
13 }
14 }
15 }
16 }
17
18 public static int dp(int i) {//从i开始的最大嵌套矩形个数
19 if(d[i]>=0) {
20 return d[i];
21 }
22 d[i]=0;
23 for(int j=0;j<d.length;j++) {
24 if(a[i][j]==1) {
25 d[i]=Math.max(d[i],1+dp(j));
26 }
27 }
28 return d[i];
29 }
30
31 public static void test(int i,String head) {//找到d[i]中的最大值,按字典序输出路径们
32 head=head+i;
33 if(d[i]==0)
34 {
35 System.out.println(head);
36 }
37 String str="";
38 for(int j=0;j<d.length;j++) {
39 if(a[i][j]==1&&d[i]==d[j]+1) {
40 test(j,head);
41 }
42 }
43 }
44 public static void main(String[] args) {
45 Scanner scn=new Scanner(System.in);
46 ErYuan[] es=new ErYuan[6];
47 for(int i=0;i<6;i++) {
48 int x=scn.nextInt();
49 int y=scn.nextInt();
50 es[i]=new ErYuan(x,y);
51 }
52 getA(es);
53 for(int i=0;i<d.length;i++) {
54 dp(i);
55 }
56 int max=0;
57 for(int i=1;i<d.length;i++) {
58 max=d[max]>=d[i]?max:i;
59 }
60 for(int i=max;i<d.length;i++) {
61 if(d[i]==d[max]) {
62 test(i,"");
63 }
64 }
65 }
1 class ErYuan{
2 int x;
3 int y;
4 public boolean isOk(ErYuan e) {
5 if((x<e.x&&y<e.y)||(e.x>y&&e.y>x)){
6 return true;
7 }
8 return false;
9
10 }
11 public ErYuan(int x, int y) {
12 super();
13 this.x = x;
14 this.y = y;
15 }
16
17 }
2.固定终点的最长路和最短路
硬币问题:有n种硬币,面值分别为v1..vn,每种都有无限多,给定非负整数S。可以选用多少个硬币,使得面值之和恰好为S?输出硬币的最小值和最大值1<=n<100,0<=S<=10000,1<=vi<=S
1 static int[]d=new int[10];
2 static int[]vis=new int[10];
3 static int[]v=new int[5];
4 /**
5 * 硬币问题:有n种硬币,面值分别为v1..vn,每种都有无限多,给定非负整数S。
6 * 可以选用多少个硬币,使得面值之和恰好为S?
7 */
8 /**
9 * S->0的路径长度
10 * @param S
11 */
12 public static int dp(int S) {
13 if(vis[S]==1)return d[S];
14 vis[S]=1;
15 for(int i=0;i<vis.length;i++)if(vis[i]<=S)d[S]=Math.max(d[S], dp(S-vis[i])+1);
16 return d[S];
17 }
如果要打印出来就同上,可以用递推或者储存的方式打印出来,储存的话用空间换取时间。
3.小结
传统的递推法可以表示成“对于每个状态i,计算f(i)",或者称为“填表法”.这需要对于每个状态i,找到f(i)依赖的所有状态。
刷表法:对于每个状态i,更新f(i)所影响的状态。只有当每个状态所依赖的对它的影响相互独立时才能用刷表法。