您的位置:首页 > 其它

ICPC2013南京邀请赛 E. Eloh 解题报告

2013-05-17 13:48 288 查看
『题目』http://icpc.njust.edu.cn/Contest/194/Problem/E

『题目大意』给出n个物品的质量mi和体积vi,选出若干个使得sum(mj)/sum(vj)(解法中称和比值)达到最大值(1<=j<=n-s)。输出不选的个数s和不选的物品编号。

『解法』01分数规划、单调队列、栈

按mi/vi排序,枚举s,并维护以下两个集合中的最值即可:

维护已选物品中比值最差值:按比值从高到低把每个物品加入队列,设当前已选物品的和比值为M/V,类似斜率优化,物品i暂时劣于物品j,且mi/vi<mj/vj => (mi-mj)/(vi-vj)<M/V

把每个物品比值看成平面上的点,如果连续3个物品比值的连线是凸的,中间那个肯定不选

1、如果队列尾存在物品比值比当前物品比值低的,扔掉(从M/V大于任何mi/vi考虑)

2、如果队列尾存在和比值比当前物品和比值低的,扔掉(比值高和比值低的一定不差)

此时可以保证,队列中mi递减,vi递减,为斜率优化创造了条件

3、如果队列尾连续3个点连线为凸,把倒数第二个扔了。保证队列中相邻点斜率递减

4、由于M/V递减,所以如果队头元素比队头+1元素优,队头扔了。此时队头元素最差

维护未选物品比值最优值:

1、如果队列尾存在和比值比当前物品和比值高的,扔掉。此时可以保证,队列中mi递增,vi递增

2、如果队列尾连续3个点连线为凹,把倒数第二个扔了。保证了队列中相邻点斜率递减

3、由于M/V递增,所以如果队尾-1元素比队尾元素优,队尾扔了。此时队尾元素最优

也就是说这部分不需要队列只要一个栈就可以了

『参考代码』

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 200002
typedef long long ll;
struct arr
{
    ll x, y;
    arr() {}
    arr(ll i, ll j):x(i), y(j) {}
    arr operator-(const arr i) const {
        return arr(x - i.x, y - i.y);
    }
    ll operator*(const arr i) const {
        return x * i.y - y * i.x;
    }
    bool operator<(const arr i) const {
        return (*this) * i > 0;
    }
} a
, b
;
int q
, p
, ans
;
int n, s;
 
int main()
{
    int i, h, t;
    ll sx, sy;
    while (scanf("%d", &n) != EOF && n)
    {
        for (i = 1; i <= n; ++i)
            scanf("%lld%lld", &a[i].y, &a[i].x);
        sort(a + 1, a + n + 1);
        sx = sy = 0;
        for (i = 1; i < n; ++i) {
            sx += a[n - i + 1].x;
            sy += a[n - i + 1].y;
            b[n - i] = arr(sx, sy);
        }
        memset(p, 0, sizeof(p));
        memset(q, 0, sizeof(q));
        t = 0;
        for (i = 1; i < n; ++i) {
            while (t && a[q[t]].x >= a[i].x) --t;
            while (t > 1 && (a[i] - a[q[t]]) * (a[q[t - 1]] - a[q[t]]) >= 0) --t;
            q[++t] = i;
            while (t > 1 && b[i] * (a[q[t]] - a[q[t - 1]]) <= 0) --t;
            p[i] = q[t];
        }
        h = 1;
        s = t = 0;
        for (i = n - 1; i; --i) {
            while (h <= t && (a[q[t]].x <= a[i + 1].x || a[q[t]].y <= a[i + 1].y)) --t;
            while (h < t && (a[i + 1] - a[q[t]]) * (a[q[t - 1]] - a[q[t]]) >= 0) --t;
            q[++t] = i + 1;
            while (h < t && b[i] * (a[q[h + 1]] - a[q[h]]) <= 0) ++h;
            if (b[i] * (a[p[i]] - a[q[h]]) > 0) ans[++s] = i;
        }
        printf("%d\n", s);
        for (i = s; i; --i)
            printf("%d\n", ans[i]);
    }
    return 0;
}


『感想』

感觉自己的报告有点不知所云了。。其实这是一道比较经典复杂的01分数规划的题,除了用单调队列、栈来做,似乎用平衡树会有更好的效果。比赛时看懂题意之后就觉得跟USACO某次月赛的某道题很像,于是找模版看有没有,结果找到了。根据模版打完改一下然后就1Y了。。所以只能说非常幸运,模版太神了=w=
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: