我该如何解决这个动态编程?

时间:2015-11-07 17:11:24

标签: algorithm dynamic-programming

我正在练习DP,我遇到了这个问题。 http://www.spoj.com/problems/MPILOT/en/

查理收购了航空运输公司并继续经营,他需要以任何可能的方式降低开支。有N名飞行员为他的公司工作(N是偶数),需要制造N / 2飞机机组人员。一名机组人员由两名飞行员组成 - 一名船长和他的助手。船长必须比他的助手年长。每个飞行员都有一份合同,给他两个可能的工资 - 一个是船长,另一个是助手。对于同一名飞行员来说,船长的工资大于助理。但是,助理可能比他的队长薪水更高。编写一个计算程序,计算查理需要为飞行员提供的最低金额'如果他决定花一些时间在船员中做出最佳(即最便宜)的飞行员安排,那么工资。

输入

输入的第一行包含整数N,2≤N≤10,000,N是偶数,即为查理公司工作的飞行员数量。接下来的N行输入包含导频&#39;工资。线路按飞行员的年龄排序,最年轻的飞行员的工资是第一个。这N行中的每一行包含由空格字符分隔的两个整数,X i Y,1≤Y<1。 X≤100,000,作为船长的工资(X)和作为助理的工资(Y)。

输出

第一个也是唯一一个输出线应该包含查理需要为飞行员提供的最低金额。薪水。

经过研究,我发现这将由DP解决,但我究竟如何解决这个问题呢?我花了几个小时阅读链接,但我没有得到一个容易理解的链接。请帮我。

4 个答案:

答案 0 :(得分:0)

像往常一样,我们需要找到一组紧凑的子问题。记住飞行员如何匹配的细节是不行的 - 我们需要类似下面的表征。

考虑一个标签,使每个飞行员成为队长或助手而不考虑匹配。当且仅当以下两个条件成立时,才存在有效匹配:

  1. 有N / 2队长和N / 2助手。
  2. 对于每个年龄段的截止日期,至少有比截止年龄小的助手,因为有些队长比截止年龄小。
  3. “唯一的”方向很简单:条件1是显而易见的,条件2成立,因为每个2名飞行员都有不平等,我们可以总结不等式。

    对于“if”方向,我们实际上必须建造船员。我们通过归纳进行。如果没有飞行员,则空匹配有效。否则,由于最年轻的飞行员是助手(条件2)并且至少有一名上尉(按条件1),(如果你想得到幻想)存在(通过斯珀纳的引理)一对飞行员,这样(a)没有飞行员的年龄中间(b)该对的年轻人是助手(c)该对的年长者是队长。匹配该对并将其从池中移除。观察到两个条件仍然存在,因此通过归纳假设匹配其余条件。

    该观察导致O(N ^ 2)时间动态程序。我们反复阅读下一个最老的飞行员的工资,然后计算,考虑到目前为止已经考虑了K个飞行员,对于从0到[K / 2]的所有C,支付K-C助手和C队长的最低费用飞行员。最后,返还支付N / 2助手和N / 2队长的费用。未经测试的Python:

    def cost(pilots):
        cost = [0]
        for i, (assistant_salary, captain_salary) in enumerate(pilots):
            cost.append(float('inf'))  # two-way sentinel
            cost = [min(cost[c] + assistant_salary,
                        cost[c - 1] + captain_salary)
                    for c in range((i + 1) / 2 + 1)]
        return cost[-1]  # i.e., N/2
    

答案 1 :(得分:0)

让我们为递归制定一些条件:如果我们到达终点,则返回总和;如果我们有N/2名助手,请接下来加上队长;如果我们拥有与队长相同数量的助手,则接下来添加一名助手(我们不能让一名队长比助理更年轻);否则,返回添加队长或助手的最低费用。

JavaScript代码:

var x = [4,5,6,7];
var y = [3,2,1,2];
var n = x.length;

function f(i,ys,s){
  if (i == n){
    return s;
  }
  if (ys == n / 2){
    return f(i + 1,ys,s + x[i]);
  } else if (i % 2 == 0 && ys == i / 2){
    return f(i + 1,ys + 1,s + y[i]);
  } else {
    return Math.min(f(i + 1,ys + 1,s + y[i]),f(i + 1,ys,s + x[i]));
  }
}

输出:

console.log(f(0,0,0)) // 16

答案 2 :(得分:0)

实际上,这是一种可视化的好方法。从左下角开始,我们可以设想选择Y(助理工资)作为向右移动的X(队长&#39; s)工资)作为一个向上运动,条件是西南 - 东北对角线没有交叉(见维基百科的Catalan Number)。

Cn is the number of monotonic lattice paths along the edges of a grid with n × n square cells, which do not pass above the diagonal. Image from Wikipedia.

由此可以看出,三角形中的每个节点最多有两个前辈,从西方或从南方,所以自下而上的一般情况应该是:

                    captain               assistant
dp[i][j] = min(x[i+j-1] + dp[i-1][j], y[i+j-1] + dp[i][j-1])

Example:

x = [4,5,6,7]
y = [3,2,1,2]

            [9+7]
     [3+5]  [min(8+1,5+6)]
[.]  [3]    [3+2]

我将编码留作练习。

答案 3 :(得分:0)

对于 C++ 中的递归方法,您可以按照以下代码更好地理解:

int min_salary(int a[],int n,int x,int c[])
{
    if(n==0)
        return 0;
    if(x==0)
        return(a[0]+min_salary(a+1,n-1,1,c+1));
    if(x==n)
        return(c[0]+min_salary(a+1,n-1,x-1,c+1));
    else
        return(min(a[0]+min_salary(a+1,n-1,x+1,c+1),c[0]+min_salary(a+1,n-1,x-1,c+1)));
}