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元素比队尾元素优,队尾扔了。此时队尾元素最优
也就是说这部分不需要队列只要一个栈就可以了
『参考代码』
『感想』
感觉自己的报告有点不知所云了。。其实这是一道比较经典复杂的01分数规划的题,除了用单调队列、栈来做,似乎用平衡树会有更好的效果。比赛时看懂题意之后就觉得跟USACO某次月赛的某道题很像,于是找模版看有没有,结果找到了。根据模版打完改一下然后就1Y了。。所以只能说非常幸运,模版太神了=w=
『题目大意』给出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=
相关文章推荐
- 2013 ACM-ICPC南京全国邀请赛
- 2013 ACM-ICPC 亚洲区域赛 成都现场赛I 解题报告
- 2013 ACM/ICPC Asia Regional Hangzhou Online(解题报告) 正在更新
- HDU_2013 ACM-ICPC南京赛区全国邀请赛——题目重现
- HDU 4588 2013 ACM/ICPC 南京邀请赛 C 题
- 2013 ACM/ICPC 成都网络赛解题报告
- 2013 ACM/ICPC 长沙网络赛B题解题报告
- 2013 ACM/ICPC南京邀请赛B题(求割点扩展)
- 2014嘉杰信息杯ACM/ICPC湖南程序设计邀请赛暨第六届湘潭市程序设计竞赛 解题报告
- 2013 ACM-ICPC南京赛区全国邀请赛
- 2013 ACM-ICPC南京赛区全国邀请赛
- 2013 ACM/ICPC 长沙赛区现场赛A题 解题报告
- hdu 4587 割点 2013 ACM-ICPC南京赛区全国邀请赛
- 2015 ACM-ICPC 上海赛区邀请赛 解题报告 (Silver)
- 2013 ACM/ICPC 成都网络赛解题报告
- 2013 ACM-ICPC南京赛区全国邀请赛——题目重现
- hdu 2013 ACM/ICPC Asia Regional Online —— Warmup解题报告
- 2013 ACM/ICPC Asia Regional Changsha Online - C(Color Representation Conversion)、E (Travelby Bi)解题报告
- HDU 4578 Transformation 解题报告(线段树,2013杭州邀请赛)
- 2013 ACM/ICPC Asia Regional Changsha Online - C(Color Representation Conversion)、E (Travelby Bi)解题报告