CJOJ 2482 【POI2000】促銷活動
Description
促銷活動遵守以下規則:
- 一個消費者 —— 想參加促銷活動的消費者,在賬單下記下他自己所付的費用,他個人的詳細情況,然後將賬單放入一個特殊的投票箱。
- 當每天促銷活動結束時,從投票箱中抽出兩張賬單:
第一張被抽出的賬單是金額最大的賬單
然後被抽出的是金額最小的賬單,對於付了金額最大賬單的這位消費者,將得到一定數目的獎金,其獎金數等於他賬單上的金額與選出的最小金額的差。
爲了避免一個消費者多次獲獎,根據上面所抽出的兩張賬單都不返回到投票箱,但是剩下的賬單還繼續參加下一天的促銷活動。
超市的售出額是巨大的,這樣可以假定,在每天結束,拿出數額最大賬單和數額最小賬之前,在投票箱內就已經至少存在了 2 張賬單。你的任務是去計算每天促銷活動投進投票箱的賬單數額的基本信息。在整個活動中開銷總數。
本題中約定:
整個活動持續了 N 天 (N<=5000) 。 第 i 天放入的帳單有 a[i] 張, a[i]<=10^5 。且 sigma(a[1]…a[n])<=10^6 。 每一天放入的帳單的面值均 <=10^6 。
Input
第一行是一個整數 n ( 1 <= n <= 5000 ),表示促銷活動歷時的天數。
以下的 n 行,每行包含若干由空格分隔的非負整數。第 i+1 行的數表示在第 i 天投入箱子的賬單金額。每行的第一個數是一個整數 k ( 0 <= k <= 10^5 ), 表示當日賬單的數目。後面的 k 個正整數代表這 k 筆賬單的金額,均小於10^6 。
整個活動中涉及到的賬單筆數不會超過 10^6 。
Output
輸出唯一一行是一個整數,等於整個促銷活動中應該付出的獎金總額。
Sample Input
5
3 1 2 3
2 1 1
4 10 5 5 1
0
1 2
Sample Output
19
Http
CJOJ:http://oj.changjun.com.cn/problem/detail/pid/2482
Source
STL,優先隊列
題目大意
一個商場搞促銷活動n天,每天選出最高賬單和最低賬單,求所有最高賬單-最低賬單的總和。
解決思路
這道題是優先隊列的運用。分別維護兩個優先隊列,一個最小值優先,一個最大值優先,每次彈出隊首元素即可。需要注意的是,一個元素是在兩個隊列中同時存在的,而有可能在這個隊列中彈出去了,而在另一個隊列中還沒有,所以要用一個Cnt數組統計一下每個元素出現的次數,若某次發現隊首元素的Cnt已經爲0了,則說明已經在另外一個隊列中彈出,將重複的去除即可。
代碼
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxN=5001;
const int inf=2147483647;
int n;
priority_queue<int,vector<int>,less<int> > Q1;//大的優先
priority_queue<int,vector<int>,greater<int> > Q2;//小的優先
int Cnt[1000001]={0};//統計每一種金額的次數
int main()
{
int K;
int value;
int Ans=0;
cin>>n;
for (int i=1;i<=n;i++)
{
scanf("%d",&K);
for (int j=1;j<=K;j++)
{
scanf("%d",&value);
Cnt[value]++;
Q1.push(value);
Q2.push(value);
}
while (Cnt[Q1.top()]==0)//如果這種金額次數已經沒有了,說明在另一個優先隊列中已經彈出直接去掉即可
Q1.pop();
while (Cnt[Q2.top()]==0)//與上面同理
Q2.pop();
Ans=Ans+Q1.top()-Q2.top();
//cout<<Q1.top()<<" "<<Q2.top()<<endl;
Cnt[Q1.top()]--;
Cnt[Q2.top()]--;
Q1.pop();
Q2.pop();
}
cout<<Ans<<endl;
return 0;
}