您的位置:首页 > 其它

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那组数据。

主要和三分法的过程有关。假设三分一段单调的区间,那么中间有两个点相等时,三分法是很容易出问题的。

所以:一、等于号的判断根据实际情况作出。比如要求这个值尽量小,那么等于时右边界也向左移。

二、特判开头结尾。

如果题目要求更苛刻一点,就可以二分答案,三分判断了~

源码:

二分答案版:

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: