[USACO 2012 Feb G]Cow Coupons----贪心&带悔(看完稳AC)

简介: 意思是有n头牛,现在有k张优惠券以及m元,每头牛有一个原始价格和折扣价格,问最多能买多少牛一开始的方法很简单,由于题目里面说了折扣价格一定比原始价格便宜,所以说首先按照折扣价格从小大大进行排序,将前k个牛的花费看作是折扣之后的价格,而将后面的花费看作是原始价格,然后重新将价值从小到大进行排序,尽可能的多选

db88c12a4f6341e7aadc12fc3d95bb0f.png


输入:


4 1 7
3 2
2 2
8 1
4 3


输出:


3


提示:

FJ has 4 cows, 1 coupon, and a budget of 7.FJ uses the coupon on cow 3 and buys cows 1, 2, and 3, for a total cost of 3 + 2 + 1 = 6.


意思是有n头牛,现在有k张优惠券以及m元,每头牛有一个原始价格和折扣价格,问最多能买多少牛


一开始的方法很简单,由于题目里面说了折扣价格一定比原始价格便宜,所以说首先按照折扣价格从小大大进行排序,将前k个牛的花费看作是折扣之后的价格,而将后面的花费看作是原始价格,然后重新将价值从小到大进行排序,尽可能的多选

然后交了一发之后ac

其实是错的


Wrong_code:


ll n, m, k;
struct node {
    int p, c;
    int flag;
    int val;
} a[maxn];
bool cmp(node a, node b) {
    return a.c < b.c;
}
bool cmp2(node a, node b) {
    return a.val < b.val;
}
int main() {
    n = read, k = read, m = read;
    for (int i = 1; i <= n; i++) {
        a[i].p = read, a[i].c = read;
        a[i].flag = 0;
    }
    sort(a + 1, a + 1 + n, cmp);
    for (int i = 1; i <= n; i++) {
        if (i <= k)
            a[i].val = a[i].c;
        else
            a[i].val = a[i].p;
    }
    ll sum = 0;
    sort(a + 1, a + 1 + n, cmp2);
    for (int i = 1; i <= n; i++) {
        sum += a[i].val;
        if(sum > m){
            printf("%d\n",i-1);
            return 0;
        }
    }
    cout << n <<endl;
    return 0;
}
/**
**/


11850421ddf54c89937bcda286d2c676.png

给了一组样例:


2 1 5
3 1
1000 2


应该是2才是,可惜我的结果是1

像这样的数据点有的是,在牛客后台也有很多错误的代码

比如向上面的这个样例,我们应该还要维护一个大根堆,将选择的优惠差值放在里面,考虑是不是可以将用过券的牛不用了,反悔掉,然后用到另一头牛上

然后写了一手交了亿发:


c39413dac63647f88e1ceb6e447ecf13.png


还是错误代码:

ll n, m, k;
struct node {
  ll p, c;
} a[maxn];
bool cmp(node a, node b) {
  return a.c < b.c;
}
bool cmp2(node a, node b) {
  return a.p < b.p;
}
priority_queue<ll> que;
int main() {
  n = read, k = read, m = read;
  for (int i = 1; i <= n; i++) {
    a[i].p = read, a[i].c = read;
  }
  sort(a + 1, a + 1 + n, cmp);
  ll sum = 0;
  for (int i = 1; i <= k; i++) {
    sum += a[i].c;
    if(sum > m) {
      printf("%d\n",i-1);
      return 0;
    }
    que.push(a[i].p - a[i].c);
  }
  //
  if(k == n) {
    printf("%lld\n",n);
    return 0;
  }
  sort(a + 1 + k, a + 1 + n, cmp2);
  for (int i = k + 1; i <= n; i++) {
    ll sub = a[i].p - a[i].c;
    if(sub > que.top()) {
      sum += que.top();
      que.pop();
      que.push(sub);
      sum += a[i].c;
    } else sum += a[i].p;
    if(sum > m) {
      printf("%d\n",i-1);
      return 0;
    }
  }
  cout << n <<endl;
  return 0;
}


然后想了个错误数据:

3 2 6
2 1
1000 3
5 1


结果应该是3才是,可惜我得出2

方案是优惠购买后两个,花费4,原始价格购买第一个,花费为6

对于上面的错误代码我们可以看到:


8b50b65bec5840d1925c2cd8850ed8c3.png


开始花费为2,买了两个折扣后为1的,然后进入之后,第三个物品可以优惠997,然后将优惠4的取消使用优惠券,加上差值之后变成了6,然后又买了折扣价值为3的物品,此时相当于给2 1物品以及1000 3物品使用了优惠券,其实是应该对1000 3和5 1使用优惠券

所以说这个代码是错的!!!而且很多晚上的代码都用了这种方法

在看博客的时候一定要审慎的看待这些代码,要有批判的眼光去学习


正确思路


首先我们将n头牛按照折扣价值从小到大进行排序

然后选折扣价值里面最小的k个进行求和,如果连折扣后的最小的k个都买不了的话,直接判断下进行输出i-1,在处理值的过程中加和记录sum,并且将优惠的价格放到小根堆里进行维护(que)


然后我们考虑剩下的n-k头牛是否可以替换掉已经用券的k头牛中的一部分


2 1 5
3 1
1000 2


比如在这个样例中,我们开始选择的是折扣价格为1的这头牛

然后将剩下的n-k头牛的原始价格和折扣价格分别放到小根堆里进行维护queP,queC

每次选取的时候,如果说优惠的最小的价格 + 当前优惠的价格甚至小于未进行优惠的最小价格,那么说明就可以替换掉

这时候就要把之前在前k头牛里面的折扣弥补回来 sum += que.top()

弥补之后,还要附上当前折扣之后的价格queC.top()

然后记得pop&&push相应的价值

具体代码如下:


AC_code:


typedef pair<int,int> PII;
ll n, m, k;
struct node {
  ll p, c;
} a[maxn];
bool cmp(node a, node b) {
  return a.c < b.c;
}
bool vis[maxn];
priority_queue<int, vector<int>, greater<int> > que;
priority_queue<PII,vector<PII>,greater<PII> >queP,queC;
int main() {
  n = read, k = read, m = read;
  for (int i = 1; i <= n; i++) {
    a[i].p = read, a[i].c = read;
  }
  sort(a + 1, a + 1 + n, cmp);
  ll sum = 0;
  for (int i = 1; i <= k; i++) {
    sum += a[i].c;
    if(sum > m) {
      printf("%d\n",i-1);
      return 0;
    }
    que.push(a[i].p - a[i].c);
  }
  for(int i=k+1; i<=n; i++) {
    queP.push({a[i].p,i});
    queC.push({a[i].c,i});
  }
//  debug(sum);
  for(int i=k+1; i<=n; i++) {
    while(vis[queC.top().second]) queC.pop();
    while(vis[queP.top().second]) queP.pop();
    int p1 = queC.top().second;
    int p2 = queP.top().second;
    int t1 = queC.top().first;
    int t2 = queP.top().first;
    t1 += que.top();
//    debug(p1);
//    debug(p2);
//    debug(t1);
//    debug(t2);
    if(t1 < t2) {
      sum += t1;
      queC.pop();
      que.pop();
      que.push(a[p1].p - a[p1].c);
      vis[p1] = 1;
    } else {
      sum += t2;
      queP.pop();
      vis[p2] = 1;
    }
//    cout << sum <<endl;
    if(sum > m) {
      printf("%d\n",i-1);
      return 0;
    }
  }
  cout << n <<endl;
  return 0;
}
/**
3 2 6
2 1
1000 3
5 1
4 2 8
1001 4
1001 4
4 4
4 4
**/




目录
相关文章
[USACO 2021.02 Feb]Problem 1. Year of the Cow
[USACO 2021.02 Feb]Problem 1. Year of the Cow
[USACO 2010 Feb S]Chocolate Eating
[USACO 2010 Feb S]Chocolate Eating
luogu CF776D The Door Problem(2-sat问题)
luogu CF776D The Door Problem(2-sat问题)
67 0
|
移动开发
洛谷P2639-[USACO09OCT]Bessie‘s Weight Problem G(01背包)
洛谷P2639-[USACO09OCT]Bessie‘s Weight Problem G(01背包)
洛谷P2639-[USACO09OCT]Bessie‘s Weight Problem G(01背包)
[USACO 2012 Feb B]Moo - 规律
题意: 某字符串由m o 两个字符构成 而且构成的字符串为前一个字符串 + m + o * (i+2) + 前一个字符串 *(i+2)指的是数量 问字符串第n个字符是什么 思路: 在从一个字符串构成下一个字符串的时候,是有一定的规律的,所以我们可以很轻松地找到规律 然后就可以递归地找,满足规律的时候直接输出
172 0
[USACO 2012 Feb B]Moo - 规律
洛谷P2871-[USACO07DEC]Charm Bracelet S(01背包模板题)
洛谷P2871-[USACO07DEC]Charm Bracelet S(01背包模板题)
洛谷P2871-[USACO07DEC]Charm Bracelet S(01背包模板题)
|
算法
HDU-1050,Moving Tables(不用贪心也AC)
HDU-1050,Moving Tables(不用贪心也AC)
洛谷P6207-[USACO06OCT] Cows on Skates G(DFS记录路径)
洛谷P6207-[USACO06OCT] Cows on Skates G(DFS记录路径)