zoukankan      html  css  js  c++  java
  • 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讲动态规划的!

  • 相关阅读:
    Mac-安装Git以及Git的配置
    Mac 安装Maven,并设置环境变量
    Mac Tab自动补全键
    Eclipse 代码快捷键模板(一)
    网易博客迁移(2011-05-27)
    前端JS插件整理
    Ajax请求二进制流并在页面展示
    IDE中使用System.getProperty()获取一些属性
    Spring Boot:快速入门(二)
    c 语言 指针 与地址
  • 原文地址:https://www.cnblogs.com/dominjune/p/4382459.html
Copyright © 2011-2022 走看看