Codeforces 609D 二分 or 三分
2015-12-22 19:55
429 查看
Codeforces 509D
题目链接:
http://codeforces.com/contest/609/problem/D
题意:
有n天,m个物品,至少选k个物品,使得代价和为s。(<=1e4)
有两种支付方式,每个物品有一种支付方式。每天有不同的支付方式代价,即换取一个单位的支付方式,需要付出x[i]的代价。每个物品在这个基础上,对一个支付方式有一些单位的消耗。(<=1e6)
现在问最少到第几天,使得存在一种选物品的方式,使得代价和小于等于s。
思路:
二分是标解,没什么好说的就是二分一下答案,然后O(m)的判断下是否合法就可以。
请看我三分大法。
因为考虑对一个确定的天数,它目前的对第一种支付的最小代价a,第二种支付最小代价b,第一种支付使用的物品代价和x,第二种支付物品使用的代价和y。那么代价的函数表示就是f = a * x + b * y。
按照标解的思想排序以后,假设在第一种支付中选取k1个代价最小的物品,那么在第二种支付中选取k - k1个。
那么,容易知道x递增y递减。
然后这就是一个带有极值点的函数,很自然就用三分法取做了。
卡死51那组数据。
主要和三分法的过程有关。假设三分一段单调的区间,那么中间有两个点相等时,三分法是很容易出问题的。
所以:一、等于号的判断根据实际情况作出。比如要求这个值尽量小,那么等于时右边界也向左移。
二、特判开头结尾。
如果题目要求更苛刻一点,就可以二分答案,三分判断了~
源码:
二分答案版:
三分版:
题目链接:
http://codeforces.com/contest/609/problem/D
题意:
有n天,m个物品,至少选k个物品,使得代价和为s。(<=1e4)
有两种支付方式,每个物品有一种支付方式。每天有不同的支付方式代价,即换取一个单位的支付方式,需要付出x[i]的代价。每个物品在这个基础上,对一个支付方式有一些单位的消耗。(<=1e6)
现在问最少到第几天,使得存在一种选物品的方式,使得代价和小于等于s。
思路:
二分是标解,没什么好说的就是二分一下答案,然后O(m)的判断下是否合法就可以。
请看我三分大法。
因为考虑对一个确定的天数,它目前的对第一种支付的最小代价a,第二种支付最小代价b,第一种支付使用的物品代价和x,第二种支付物品使用的代价和y。那么代价的函数表示就是f = a * x + b * y。
按照标解的思想排序以后,假设在第一种支付中选取k1个代价最小的物品,那么在第二种支付中选取k - k1个。
那么,容易知道x递增y递减。
然后这就是一个带有极值点的函数,很自然就用三分法取做了。
卡死51那组数据。
主要和三分法的过程有关。假设三分一段单调的区间,那么中间有两个点相等时,三分法是很容易出问题的。
所以:一、等于号的判断根据实际情况作出。比如要求这个值尽量小,那么等于时右边界也向左移。
二、特判开头结尾。
如果题目要求更苛刻一点,就可以二分答案,三分判断了~
源码:
二分答案版:
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <string> #include <algorithm> #include <iostream> using namespace std; #define inf (1000000007) #define LL long long const int MAXN = 200000 + 5; int c1[MAXN], c2[MAXN]; int cost1[MAXN], cost2[MAXN]; int re1[MAXN], re2[MAXN]; int use[MAXN], reflag[MAXN]; int cnt1, cnt2; LL sum1[MAXN], sum2[MAXN]; int vis[MAXN]; int n, m, k, s; struct D { int u, v; }d1[MAXN], d2[MAXN]; bool cmp(D a, D b){return a.v < b.v;} int solve(int u, int &mark) { LL ans = s + 1; int t1, t2; t1 = min(cnt1, k), t2 = k - t1; mark = 0; while(t2 <= k && t2 <= cnt2 && t1 >= 0){ LL temp = sum1[t1] * cost1[u] + sum2[t2] * cost2[u]; if(ans > temp) ans = temp, mark = t2; t2++, t1--; // t1 = max(t1, 0); } // printf("u = %d, ans = %I64d\n", u, ans); // system("pause"); if(ans > s) return s + 1; int temp = ans; return temp; } int main() { while(scanf("%d%d%d%d", &n, &m, &k, &s) != EOF){ cost1[0] = cost2[0] = inf; for(int i = 1 ; i <= n ; i++){ scanf("%d", &c1[i]); if(cost1[i - 1] < c1[i]) cost1[i] = cost1[i - 1], re1[i] = re1[i - 1]; else cost1[i] = c1[i], re1[i] = i; } for(int i = 1 ; i <= n ; i++){ scanf("%d", &c2[i]); if(cost2[i - 1] < c2[i]) cost2[i] = cost2[i - 1], re2[i] = re2[i - 1]; else cost2[i] = c2[i], re2[i] = i; } cnt1 = cnt2 = 0; int u, v; for(int i = 1 ; i <= m ; i++){ scanf("%d%d", &u, &v); if(u == 1) d1[++cnt1].u = i, d1[cnt1].v = v; else d2[++cnt2].u = i, d2[cnt2].v = v; reflag[i] = u; } sort(d1 + 1 , d1 + cnt1 + 1, cmp); sort(d2 + 1 , d2 + cnt2 + 1, cmp); sum1[0] = sum2[0] = 0; for(int i = 1 ; i <= cnt1 ; i++) sum1[i] = sum1[i - 1] + d1[i].v; for(int i = 1 ; i <= cnt2 ; i++) sum2[i] = sum2[i - 1] + d2[i].v; int ans; int le = 1, re = n; while(le < re - 1){ int mid = (le + re) >> 1; // printf("le = %d, re = %d, solve = %d\n", le, re, solve(mid, ans)); if(solve(mid, ans) <= s) re = mid; else le = mid; } int mark; ans = -1; if(solve(le, mark) <= s) ans = le; else if(solve(re, mark) <= s) ans = re; if(ans == -1){ printf("%d\n", ans); continue; } else{ for(int i = 1 ; i <= m ; i++) vis[i] = 0; for(int i = 1 ; i <= k - mark ; i++) vis[d1[i].u] = 1; for(int j = 1 ; j <= mark ; j++) vis[d2[j].u] = 1; printf("%d\n", ans); for(int i = 1 ; i <= m ; i++){ if(vis[i]){ if(reflag[i] == 1) printf("%d %d\n", i, re1[ans]); else printf("%d %d\n", i, re2[ans]); } } } } return 0; }
三分版:
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <string> #include <algorithm> #include <iostream> #include <queue> using namespace std; #define inf (1000000007) #define LL long long const int MAXN = 200000 + 5; struct D { int u, v; }a1[MAXN], a2[MAXN]; int x[MAXN], y[MAXN]; int costx[MAXN], costy[MAXN]; ///前i天中最小的第一支付和第二支付代价 int rex[MAXN], rey[MAXN]; ///最小支付代价的那一天 int reflag[MAXN]; ///记录第几个物品是属于第一种支付方式还是第二种 LL sum1[MAXN], sum2[MAXN]; ///前缀和 int cnt1, cnt2; ///两种支付方式的物品数量和 int re1[MAXN], re2[MAXN]; ///记录数组 int vis[MAXN]; ///哪些物品使用过 LL check(int mark, int tx, int ty) { return sum1[re1[mark]] * tx + sum2[re2[mark]] * ty; } bool cmp(D a, D b){return a.v < b.v;} int main() { int n, m, k, s; while(scanf("%d%d%d%d", &n, &m, &k, &s) != EOF){ costx[0] = costy[0] = inf; for(int i = 1 ; i <= n ; i++){ scanf("%d", &x[i]); if(x[i] > costx[i - 1]) costx[i] = costx[i - 1], rex[i] = rex[i - 1]; else costx[i] = x[i], rex[i] = i; } for(int i = 1 ; i <= n ; i++){ scanf("%d", &y[i]); if(y[i] > costy[i - 1]) costy[i] = costy[i - 1], rey[i] = rey[i - 1]; else costy[i] = y[i], rey[i] = i; } int u, v; cnt1 = cnt2 = 0; for(int i = 1 ; i <= m ; i++){ scanf("%d%d", &u, &v); reflag[i] = u; if(u == 1) a1[++cnt1].v = v, a1[cnt1].u = i; else a2[++cnt2].v = v, a2[cnt2].u = i; } sort(a1 + 1, a1 + cnt1 + 1, cmp); sort(a2 + 1, a2 + cnt2 + 1, cmp); sum1[0] = sum2[0] = 0; for(int i = 1 ; i <= cnt1 ; i++) sum1[i] = sum1[i - 1] + a1[i].v; for(int i = 1 ; i <= cnt2 ; i++) sum2[i] = sum2[i - 1] + a2[i].v; // printf("check cnt1\n"); // for(int i = 1 ; i <= cnt1 ; i++) printf("i = %d, u = %d, v = %d\n", i, a1[i].u, a1[i].v); // printf("check cnt1\ncheck cnt2\n"); // for(int i = 1 ; i <= cnt2 ; i++) printf("i = %d, u = %d, v = %d\n", i, a2[i].u, a2[i].v); // printf("check cnt2\n"); int t1, t2; t1 = min(k, cnt1), t2 = k - t1; int ans = n + 1; int cnt = 0; // if(n == 1 && m == 200000 && k == 100000 && s == 100000){ // if(a2[cnt2].v > 1) printf("hellohello\n"); // if(a1[cnt1].v > 1) printf("sdafa\n"); // } // printf("t1 = %d, t2 = %d\n", t1, t2); re1[0] = re2[0] = 0; while(t2 <= cnt2 && t2 <= k){ re1[++cnt] = max(0, t1), re2[cnt] = t2; t1--, t2++; } // if(n == 1 && m == 200000 && k == 100000 && s == 100000) // printf("cnt1 = %d, cnt2 = %d, cnt = %d\n", cnt1, cnt2, cnt); for(int i = 1 ; i <= n ; i++){ int le = 1, re = cnt; while(le < re - 1){ int t1 = (le + re) >> 1; int t2 = (t1 + re) >> 1; // int l = (re - le + 1) / 3; // int t1 = le + l, t2 = re - l; // printf("le = %d, re = %d, t1 = %d, t2 = %d\n", le, re, t1, t2); // if(n == 1 && m == 200000 && k == 100000 && s == 100000) // printf("ch1 = %I64d, ch2 = %I64d, le = %d, re = %d, t1 = %d, t2 = %d\n", ch1, ch2, le, re, t1, t2); if(check(t1, costx[i], costy[i]) >= check(t2, costx[i], costy[i])) le = t1; // else if(ch1 == ch2) le = t1, re = t2; else re = t2; } int mark = -1; if(check(le, costx[i], costy[i]) <= s) mark = le; else if(check(re, costx[i], costy[i]) <= s) mark = re; else if(check(1, costx[i], costy[i]) <= s) mark = 1; else if(check(cnt, costx[i], costy[i]) <= s) mark = n; // if(n == 1 && m == 200000 && k == 100000 && s == 100000){ // printf("i = %d, le = %d, re = %d, check(le) = %I64d, check(re) = %I64d\n", i, le, re, check(le, costx[i], costy[i]), check(re, costx[i], costy[i])); // printf("mark = %d\n", mark);} if(mark == -1) continue; ans = i; for(int j = 1 ; j <= m ; j++) vis[j] = 0; for(int t1 = 1 ; t1 <= re1[mark] ; t1++) vis[a1[t1].u] = 1; for(int t2 = 1 ; t2 <= re2[mark] ; t2++) vis[a2[t2].u] = 1; break; } if(ans > n){ printf("-1\n"); } else{ printf("%d\n", ans); for(int i = 1 ; i <= m ; i++){ if(vis[i]){ if(reflag[i] == 1) printf("%d %d\n", i, rex[ans]); else printf("%d %d\n", i, rey[ans]); } } } } return 0; }
相关文章推荐
- QT学习之路(6)构建文件对话框
- Effective minidump
- hdu 1027 Ignatius and the Princess II【dfs 排列组合】
- Spring+Mybatis实现动态SQL查询
- 设计模式入门之装饰器模式Decorator
- 基于代理类ProxyBean的AOP的实现
- HDU1286-找新朋友(欧拉函数)
- String和CharSequence
- 图标制作、icon制作、(简易、高效的图标制作软件)
- 高效程序员的7个共同特征
- Javascript之预编译
- Android自助餐之点击事件传递
- 关于登录界面的背景图片设置问题
- Hive差集工具
- ORA-01031: insufficient privileges
- 分享:HTML常见面试题
- Windows定时开关机设置
- cocos2d-js 学习笔记 --安装调试(2)
- exchange邮箱系统增加验证码机制
- 分享 :CSS常见面试题