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\n", f[n][m]);
37     }
38
39     return 0;
40 }                                 

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

时间: 2024-12-22 01:07:27

sicily 1828 Minimal(dp)的相关文章

编程题目分类(剪辑)

1. 编程入门 2. 数据结构 3. 字符串 4. 排序 5. 图遍历 6. 图算法 7. 搜索:剪枝,启发式搜索 8. 动态规划/递推 9. 分治/递归 10. 贪心 11. 模拟 12. 算术与代数 13. 组合问题 14. 数论 15. 网格,几何,计算几何 [编程入门] PC 110101, uva 100, The 3n+1 problem, 难度 1 PC 110102, uva 10189, Minesweeper, 难度 1 PC 110103, uva 10137, The T

Sicily 1146:Lenny&#39;s Lucky Lotto(dp)

题意:给出N,M,问有多少个长度为N的整数序列,满足所有数都在[1,M]内,并且每一个数至少是前一个数的两倍.例如给出N=4, M=10, 则有4个长度为4的整数序列满足条件: [1, 2, 4, 8], [1, 2, 4, 9], [1, 2, 4, 10], [1, 2, 5, 10] 分析:可用动态规划解题,假设dp[i][j],代表满足以整数i为尾数,长度为j的序列的个数(其中每一个数至少是前一个数的两倍).那么对于整数i,dp[i][j] 等于所有dp[k][j-1]的和,其中k满足:

Timus 1303 Minimal Coverage DP或贪心

1303. Minimal Coverage Given set of line segments [Li, Ri] with integer coordinates of their end points. Your task is to find the minimal subset of the given set which covers segment [0, M] completely (M is a positive integer). Input First line of th

Sicily 1345:能量项链(dp)

#include <bits/stdc++.h> using namespace std; int main(){ int n; while(cin >> n){ int arr[n]; for(int i = 0; i < n; i++)cin >> arr[i]; int dp[n][n+1]; //使用dp[i][j]代表从第i个数字往后合并j位的最大能量 memset(dp, 0, sizeof(dp)); for(int i = 0; i < n;

hihocoder 1828 Saving Tang Monk II (DP+BFS)

题目链接 Problem Description <Journey to the West>(also <Monkey>) is one of the Four Great Classical Novels of Chinese literature. It was written by Wu Cheng'en during the Ming Dynasty. In this novel, Monkey King Sun Wukong, pig Zhu Bajie and Sha

Sicily 1001(dp)

题目连接:http://soj.sysu.edu.cn/1001 解题报告:f[i]表示第i个字母前能组成的种类 情况:1.s[i]==0,f[i]=f[i-1] 2.s[i-1]*10+s[i]>=10&&<=26,f[i]=f[i-1]+f[i-2] 3.!s[i-1]*10+s[i]>=10&&<=26,f[i]=f[i-1]; #include <iostream> #include <cstring> #includ

UVa 116 Unidirectional TSP(DP)

题意  一个n*m的环形矩阵(第一行和最后一行是相邻的)  从第一列任意位置出发  只能往右上,右,右下3个方向走  求走到第m列经过的的最小数字和 基础DP  横着的数塔问题 #include <bits/stdc++.h> #define l(x) d[x][j+1] using namespace std; const int N = 105; int n, m, g[N][N], d[N][N], fol[N][N]; int main() { int n, m, u, b, t, k

zoj 3469 Food Delivery(区间dp)

Food Delivery Time Limit: 2 Seconds      Memory Limit: 65536 KB When we are focusing on solving problems, we usually prefer to stay in front of computers rather than go out for lunch. At this time, we may call for food delivery. Suppose there are N p

URAL 1326. Bottle Taps(简单的状压dp)

题目不太好读懂,就是先给你一个n代表要从n个物品中买东西,然后告诉你这n个东西的单价,在给你m个集合的情况,就是每个结合中有x件物品,他们合起来买的价格是k.这x件物品依次是:p1--px.之后给你一个kk,表示你要买的物品的编号.让你求出来如何花费最少的钱买到要求的序列. 20,可以状压啊,注意一开始的时候先把单价的状态处理出来...之后就是水题了啊. 1326. Bottle Taps Time limit: 3.0 second Memory limit: 64 MB Programmer