hdu 3866 Moonfang's Birthday 排序?贪心?二分?思维?

来源:互联网 发布:ledrom编辑软件 编辑:程序博客网 时间:2024/05/01 15:36





Moonfang's Birthday

Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 1034    Accepted Submission(s): 410


Problem Description
It's Moonfang's birthday,and his friends decided to buy him a copy of XianJianQiXiaZhuan V.
  
Since some of friends have more money available than others, nobody has to pay more than he can afford. Every contribution will be a multiple of 1 cent,i.e.,nobody can pay fractions of a cent.
  
Everybody writes down the maxumum amount he is able to contribute. Taking into account these maximum amounts from everybody, your task is to share the cost of the present as fairly as possible. That means, you minimize the largest distance of the contributions to 1/n-th of the total cost.
  
In case of a tie, minimize the second largest distance, and so on. Since the smallest unit of contribution is 1 cent, there might be more than one possible division of the cost. In that case, persons with a higher maximum amount pay more. If there is still ambiguity, those who come first in the list pay more.
  
Since you bought the present, it is your task to figure out how much everybody has to pay.
 

Input
On the first line a positive integer: the number of test cases, at most 200. After that per test case:
  • One line with two integers p and n: the price of the present in cents (1 ≤ p ≤ 1 000 000) and the number of people (2 ≤ n ≤ 10000) who contribute to the present (including you).
  • One line with n integers ai (1 ≤ ai ≤ 1 000 000), where ai is the maximum amount, in cents, that the i-th person on the list is able to contribute.
 

Output
Per test case: 
  • One line with n integers: the amounts each person has to contribute according to the scheme. If the total cost cannot be divided according to the above rules, the line must contain "IMPOSSIBLE" instead.
 

Sample Input
3 20 4 10 10 4 4 7 3 1 1 4 34 5 9 8 9 9 4
 

Sample Output
6 6 4 4 IMPOSSIBLE 8 7 8 7 4
 

Source
2011 Multi-University Training Contest 3 - Host by BIT
 

Recommend
lcy   |   We have carefully selected several similar problems for you:  3863 3868 3861 3865 3860 
 

Statistic | Submit | Discuss | Note


题意:这个题题意非常含糊不清,


说的是n个人要凑齐P元,所求是每个人的支付钱数。


要求:

your task is to share the cost of the present as fairly as possible. That means, you minimize the largest distance of the contributions to 1/n-th of the total cost.
  
In case of a tie, minimize the second largest distance, and so on. Since the smallest unit of contribution is 1 cent, there might be more than one possible division of the cost. In that case, persons with a higher maximum amount pay more. If there is still ambiguity, those who come first in the list pay more.



这个题意似乎分为两部分,第一个部分是使得每人出钱数尽量平均。

但是后面有一个that means,这就引出了第二种解法:

that means就是这就是说嘛,就是等价于

就是满足钱最多的人付钱最多,钱次多的人付钱次多....钱最少的人付钱最少(>=关系)

如果两个人钱一样多,那么序号小的付钱多(至少不小于序号大的)


这两种要求似乎并不是很显然的等价。

给出两份ac代码。


对于这个要求有的人考虑的主要是

your task is to share the cost of the present as fairly as possible

就是以均分来考虑。代码如下:思想在注释里。


/**这种方法是求出一个理想平均值,即每人出钱数ave(整形),然后还以一个零钱总数leave。首先考虑钱数小于ave的人,为了使平均,他们必须出自己所有的钱。因为即使出了所有的钱,他们出的钱都是不够的。如此一来,零钱就会变多。零钱相当于是剩下的人  每人除了支付理想ave,额外还一共需要填补的钱。对于处于钱数处于ave上方(大于ave)的人,考虑他们付钱时首先他们一定能付ave,其次,他们要尽量填平零钱这个坑。假设现在考虑了i-1个人,而且剩下n-i+1个人的支付能力均大于理想均值ave 那么剩下的人最平均的支付方式方式是:  ave+leave/(n-i+1) 即每人出这么多钱。但是你要知道这可能不是一个整数。 不过题目要求 :相比前面,越后面的人,出的钱数要越多。 所以即使现在这个值  ave+leave*1.0/(n-i+1)  是小数,我们也应该floor(),也就是向下取整。 否则(向上取整)第i个人后面的人(n-i个人)实际付出的均值就比ave+leave*1.0/(n-i+1)要小, 也小于第i个人付的钱,也就是说后面总有一个人付的钱比第i个人大,这不符合题意。 还有一点就是对于最后一个人 ave+leave*1.0/(n-i+1) 一定是整数 准确地说是   (n-i+1)|leave**/#include<cstdio>#include<string>#include<cstring>#include<iostream>#include<cmath>#include<algorithm>#include<vector>using namespace std;#define all(x) (x).begin(), (x).end()#define for0(a, n) for (int (a) = 0; (a) < (n); (a)++)#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)typedef long long ll;typedef pair<int, int> pii;const int INF =0x3f3f3f3f;const int maxn= 10000   ;int ans[maxn+10];int p,n;struct Node{    int money;    int ind;    Node(){}    Node(int money,int ind):money(money),ind(ind){}   bool operator<(const Node y)const   {        if(money!=y.money)        {            return money<y.money;        }        return ind>y.ind;   }} a[maxn+10];int main(){    int T;scanf("%d",&T);    while(T--)    {        ll sum=0;        scanf("%d%d",&p,&n);        for1(i,n)        {            scanf("%d",&a[i].money);            a[i].ind=i;            sum+=a[i].money;        }        if(sum<p)//根本就付不起        {            puts("IMPOSSIBLE");            continue;        }        ll ave=p/n;//理想ave        ll leave=p%n;//零钱数        sort(a+1,a+1+n);        for1(i,n)        {            if(a[i].money<ave)            {                ans[a[i].ind ]=a[i].money;                leave+=ave-a[i].money;                continue;            }            if(a[i].money< ave+ leave/(n-i+1) )            {                ans[a[i].ind ]=   a[i].money;                leave-=a[i].money-ave;            }            else            {                ans[a[i].ind ]=ave+leave/(n-i+1);                leave-= leave/(n-i+1);            }        }        for1(i,n)        {            if(i!=1)  putchar(' ');            printf("%d",ans[i]);        }        putchar('\n');    }   return 0;}



第二种方法是排序后从左往右考虑,始终保证当前人出的钱尽量少,又大于等于剩下的人出钱数。

这样考虑比较复杂,二分之中套二分,外加预处理。

第一个二分:分当前人出钱数 ,第二个二分:分  钱数大于等于当前人试探出钱数 的最右边的人。


外加前缀和sum[x]预处理。



#include<cstdio>#include<cstring>#include<string>#include<iostream>#include<sstream>#include<algorithm>#include<utility>#include<vector>#include<set>#include<map>#include<queue>#include<cmath>#include<iterator>#include<stack>using namespace std;const int INF=1e9+7;const double eps=1e-7;int p,n;const int maxn=10000;typedef long long ll;int ans[maxn+10];struct Node{   int ind;   int val;   bool operator<(const Node y)const   {       if(val!=y.val)       {           return val>y.val;       }       return ind<y.ind;   }}a[maxn+10];ll s[maxn+10];int bs2(int le,int ri,int x){    while(le<=ri)    {        int mid=(le+ri)>>1;        if( a[mid].val>=x)   le=mid+1;        else ri=mid-1;    }    return ri;}bool can(int st,int leave,int mid){    int p=bs2(st,n,mid);//大于等于mid的最后一个位置     ll tmp=(ll)mid*(p-st+1)+s[n]-s[p];     return tmp>=leave;}int find(int st,int leave,int le,int ri){    while(le<=ri)    {        int mid=(le+ri)>>1;        if(can(st,leave,mid))  ri=mid-1;        else le=mid+1;    }    return le;}bool work(){    int limit=a[1].val;    int leave=p;    for(int i=1;i<=n;i++)    {        limit=min(limit,a[i].val);       ans[ a[i].ind  ]=find(i, leave,0, limit  );        if(ans[a[i].ind]>limit)  return false;       limit=ans[a[i].ind ];       leave-=ans[a[i].ind];    }   return true;}void pre(){    s[0]=0;    for(int i=1;i<=n;i++)    {        s[i]=s[i-1]+a[i].val;    }}void print(){   for(int i=1;i<=n;i++)   {       if(i!=1)  putchar(' ');       printf("%d",ans[i]);   }   putchar('\n');}int main(){    int T;scanf("%d",&T);    while(T--)    {        scanf("%d%d",&p,&n);        for(int i=1;i<=n;i++)        {              scanf("%d",&a[i].val);              a[i].ind=i;        }        sort(a+1,a+1+n);        pre();         bool ok=work();         if(!ok)  puts("IMPOSSIBLE");         else print();    }    return 0;}


0 0