寻找能够获得最低工资的安排

时间:2014-08-18 18:04:30

标签: algorithm dynamic-programming greedy

我再一次遇到了一个问题pilots

问题陈述是......

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

输入

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

输出

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

样品

输入

4 
5000 3000 
6000 2000 
8000 1000 
9000 6000 

输出 19000

输入

6 
10000 7000 
9000 3000 
6000 4000 
5000 1000 
9000 3000 
8000 6000 

输出 32000

现在我正在使用一个贪婪的approch,因为很明显第一名飞行员应该是助手,之后我检查是否有可能通过让飞行员队长如果是,那么我将使他成为队长其他助手

对于大多数情况来说,这是完美的工作,但我得到了一个错误的芒果。

我的代码是..

#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define FOR(i,n) for(int i=0;i<n;i++)

int main()
{
    int n;
    //freopen("input.txt","r",stdin);
    cin>>n;
    vector<pair<pii,int> > data;
    FOR(i,n)
    {
        int csal,asal;
        cin>>csal>>asal;
        int diff=csal-asal;
        data.pb(mp(mp(csal,asal),diff));
    }
    int ccount=0,acount=0,sal=0;
    FOR(i,n)
    {
        if(acount<n/2)
        {
            int flag=1;
            for(int j=i+1;j<=i+(acount-ccount);j++)
            {
                if(data[i].se<=data[j].se)
                {
                    //cout<<j<<" ";
                    flag=0;
                    break;
                }
            }
            if(flag)
            {
                sal+=data[i].fi.se;
                acount++;
            }
            else
            {
                sal+=data[i].fi.fi;
                ccount++;
            }
        }
        else
        {
            sal+=data[i].fi.fi;
                ccount++;
        }
        //cout<<sal<<" "<<i<<"\n";
    }
    cout<<sal<<"\n";
    return 0;
}

请帮我解决这个问题..

1 个答案:

答案 0 :(得分:1)

这是一个O(N log N)运行时间的解决方案。它使用贪婪算法,但它与你的不同。

1)我们假设delta[i] = X[i] - Y[i] 2)现在让我们的进程导频按delta[i]降序排序。我们将假设所有飞行员最初都获得了船长职位 3)如果可能,应将每名飞行员重新分配到助理位置 就是这样。我声称这个算法总能给出正确的结果。

证明:
1)当飞行员无法获得助理工作时? i)当没有&#34; free&#34;他的右边(就年龄而言)的船长(也就是说,如果他右边的船长人数大于或等于助理人数)。为什么会这样?只有当有太多&#34;他右边的助手。让我们假设我们决定解决它。这需要将其中一名助手换成队长。但如果飞行员是助手,他会在当前飞行员面前进行处理。这意味着他的delta更大(回想一下,我们按排序顺序迭代它们)。还有一个观察:一名助手只能阻挡一名船长(因为船员只包括一名船长)。这两个观察结果表明,当不可能让当前的飞行员成为助手时改变某些东西只会使答案变得更糟 ii)当他被左边的助手(年龄)从他身上当作队长时。为了解决这个问题,有必要让那个人再次成为队长,就像i)一样。它会使答案变得更糟(证据与上面的情况相同) 2)使用上面的数学归纳和两个观察,可以严格证明该算法是正确的 3)此算法将始终生成精确N / 2助手,因为它会尽可能使飞行员成为助手,并且确实有N / 2种可能性。

剩下的唯一问题是:如何检查是否可以让当前的飞行员成为助手?  这是一种快速的方法:如果他是队长,则让每个飞行员分配1,否则分配-1。让我们按照飞行员的顺序看一下这个-11的数组&#39;年龄。我声称如果有一个负和的后缀,配置无效。否则,它是有效的(这个陈述并不难以证明,但我不会在这里发布证据或在我的答案中会有太多证据)。因此,我们需要维护两个操作:将-1更改为1(反之亦然),并判断是否存在带负数的后缀。这是一个标准问题,可以使用分段树来解决(您可以在每个节点中存储总和和最小后缀和,更新很容易,因为一次只更改一个元素的值(即,叶子中的值) ))。

复杂性为O(N log N),因为我们需要对数组进行排序,并且在迭代期间,每个步骤最多可以对一个分段树进行3次查询(并且每个查询需要O(log N)次)。