sicily 1828 Minimal(dp)

  最近在学习动态规划,那这题当然是动态规划了(不好意思剧透了)......刚开始做动态规划,想详细地分析一下,以便理解更深刻!

  首先读题,题目意思是要求从两个集合s1,s2选出N个数对,他们的距离(差的绝对值)的和最小;因为s1集合小于s2集合,所以就是从s1中选出全部n个数字,从s2中也选出n个数字,两两配对后求出min的值,题目就是这个意思了...

然后,我会觉得很难,因为你不知道怎么选才是最优的,那想到这里当然得先模拟一下题目意思,假设一些样例:

s1: 1 2

s2: 1 2 3

很明显,这里是先排好序的,毕竟是要求距离尽量小,那当然得先排序!很容易看出来,s1的1 2和s2的1 2配对是最好的,min=0;那是不是就是选前面的同样的个数就可以呢?当然不是,举个反例:

s1: 20 40

s2: 10 30 45

s1选20 40,s2选10 30当然不是最好的,s1选20 40,s2选10 45都比他好对吧!所以,真的不知道怎么选...

-----------------------------------------------------------------------------------------------------------------------------------

那就从小的开始想起:

s1: 3

s2: 1

这时候,n=m,个数相等,当然是直接配对了!倘若s2加多了一个2变成:

s1: 3

s2: 1 2

这时候就不选1了,选2,就相当于在原来的状态下,增加了一个数,再比较新的状态下的min,发现3-1>3-2,于是选择3 2,抛弃了1;

这时候再来个猛的,上下同时加,变成:

s1: 3 4

s2: 1 2 8

如果在原来的基础上选择4 8,min=1+8-4=5;又或者抛弃掉那个8(不能抛弃4,因为s1是全部都要用的),那就变成了n=m,直接对应就行了,这时min=4,当然优于前面那种选择!

----------------------------------------------------------------------------------------------------------------------------------------------

  其实这里开始有一点动态规划的意思了,对于某个状态,你知道了他的最优值,那么在下一个状态(相当于新加了两个数)你得通过这个最优状态求出下一个状态的最优解,如此递推下去..........

  接着继续分析:如果n=m,恰好,那就直接配对了,之前说过了;

所以重点来了,我们要分析n<m的情况,不管m比n大多少,m总是比n大的,形式如下图:

s1:****

s2:*********

因为下一个状态的最优解得由上一个状态的最优解觉得,那何不先求出一开始的初始状态呢?!那么我们假设下图就是初始状态的最优解,上图是终极状态:

s1:*

s2:**

先定义一个dp[i][j],代表用s2的前j个数配对s1的那i个数(j>i),此时i=1,j=2;此时为min=dp[1][2];那下一个状态呢?就是上下都加一个数,或者只在下面加一个数(其实上下都加的情况也包含了这个,看后面的状态状态转移方程就知道了,现在还没求出来,别急),下一个状态的图解为:

s1:*ai

s2:*(*)bj

o为新加的两个数,即是向终极状态靠近的过程,这是i=2,j=3,那dp[2][3]等于多少呢?其实有两种可能:1.ai刚好和bi配对,因为他们比较接近,

此时很明显dp[2][3]=dp[1][2]+abs(ai-bj);2.由于ai和bi差距太远,还不如和bj前面的数配对,此时dp[2][3]=dp[2][2],dp[2][2]则是前两个配对的值,这个我们之前说过了,此时i==j,可以直接得出值了!于是来了!dp[2][3]=min(dp[1][2]+abs(ai-bj), dp[2][2]),抽象一点就是:

dp[i+1][j+1] = min(dp[i][j]+abs(ai-bj), dp[i+1][j])

或者:dp[i][j] = min(dp[i-1][j-1]+abs(ai-bj), dp[i][j-1])

其实上面的状态转移的分析可以有很多种的,不过都是一个道理:

s1:**ai

s2:*******(*)bj

s1:*

s2:*******bj

这些都是一样的转移方程!

所以,我们只需要把dp[i][j]的初始值(i==j)求出来,再用递推求解dp[i][j](i<j),最后的dp[n][m]就是我们要求的min了!

到这里,初始状态有了,状态转移方程有了,递推方向有了,大功告成了,附上代码!!!

最后发现,动态规划有点像非线性方程的求解,先有一个initial guess,然后迭代求解,不知道这样想对不对呢...

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cmath>
 5 #include <cstring>
 6 
 7 using namespace std;
 8 
 9 int a[505], b[505];
10 int f[505][505];
11 
12 int main()
13 {
14     int t, n, m;
15     scanf("%d", &t);
16     while(t--)
17     {
18         
19         scanf("%d%d", &n, &m);
20         memset(f, 0, sizeof(f));
21         for(int i=1; i<=n; i++)
22             scanf("%d", &a[i]);
23         for(int i=1; i<=m; i++)
24             scanf("%d", &b[i]);
25             
26         sort(a+1, a+n+1);
27         sort(b+1, b+m+1);
28         
29         f[1][1] = abs(a[1]-b[1]);
30         for(int i=2; i<=n; i++)
31                 f[i][i] = f[i-1][i-1] + abs(a[i]-b[i]);
32                 
33         for(int i=1; i<=n; i++)
34             for(int j=i+1; j<=m; j++)
35                 f[i][j] = min(f[i][j-1], f[i-1][j-1]+abs(a[i]-b[j]));
36         printf("%d
", f[n][m]);
37     }
38     
39     return 0;
40 }                                 

最后推荐一个blog讲动态规划的!

原文地址:https://www.cnblogs.com/dominjune/p/4382459.html