最近在学习动态规划,那这题当然是动态规划了(不好意思剧透了)......刚开始做动态规划,想详细地分析一下,以便理解更深刻!
首先读题,题目意思是要求从两个集合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\n", f[n][m]); 37 } 38 39 return 0; 40 }
最后推荐一个blog讲动态规划的!
原文:http://www.cnblogs.com/dominjune/p/4382459.html