Greaterlee

博客园 首页 联系 订阅 管理

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

题目意思是说,有n个人来分担一笔开销,做到尽量公平,因为有些人钱不够多,所以只能由钱多的稍稍多分担,但是要保证付钱最多的人付的钱树和钱少的人付的钱数差值最小。
这个可以这么思考,每个人平均要分担per元,如果你带的钱不到per元,可以让别人分担,为了最公平,你不能让别人分担太多了,你要把自己所有钱拿出来(虽然还是要让别人为你贴钱),然后你可以走了,把账单扔给钱多的人让他们自己商量。他们一商量,发现剩下人平均每人付per1元(肯定大于per),那些钱不够的自觉把钱都拿出来然后走人把单子甩给剩下的人。。。终于剩下几个非常有钱的老板们,发现他们带的钱都大于pern(per每次都会提高),于是他们几个人一人出per元,公平的把账结了。

这么想的话,每个人出到自己最大的额度,又没让某个人出的过多,所以是最公平的方案。
至于实现,就是用结构体记录钱数,和标号,因为出钱的顺序是按照钱少的先尽力出,输入输出再按照标号排序输入输出就行。

那么见AC代码:

include

include

include

using namespace std;
struct node
{
int val,pos;
} p[10009],pp[10009];//pp是一个备份,最后减去p的改变量就是出钱数
int cmp1(node a,node b)//按钱排序
{
if(a.val>b.val)
return 1;
if(a.val<b.val)
return 0;
return a.pos<b.pos;

}
int cmp2(node a,node b)//按标号排序
{
return a.pos<b.pos;
}
int main()
{
int mon,n;
int T;
cin>>T;
while(T--)
{
cin>>mon>>n;
long long sum=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&p[i].val);
p[i].pos=i;
sum+=(long long)p[i].val;
pp[i]=p[i];
}
if(sum<(long long)mon)//所有人钱加起来还不够就尴尬了
{
cout<<"IMPOSSIBLE"<<endl;
continue;
}
int temp=mon;//要付多少钱
int cnt=0;//走了多少人
sort(p+1,p+n+1,cmp1);//按钱排好序
while(temp)
{
int per=temp/(n-cnt);//调整平均值
if(p[n-cnt].val>per)//如果钱最少的人也够,相当于都是老板了,该平摊了
{
for(int i=1;i<=n-cnt;i++)
p[i].val-=per,temp-=per;
for(int i=1;i<=temp;i++)//整除会使per小于等于真实值,其实还差那么几块钱,钱多的一人出一块
p[i].val--;
break;
}
else//有人不够,先尽力付
{
for(int i=n-cnt;i>0;i--)
{
if(p[i].val<=per)
{

                        temp-=p[i].val,cnt++,p[i].val=0;
                       }
                    else//钱不够的都走了,该剩下人商量了
                        break;
                }
            }
        }
    sort(p+1,p+n+1,cmp2);//按位置重新站好
    for(int i=1;i<n;i++)
    printf("%d ",pp[i].val-p[i].val);
    printf("%d\n",pp[n].val-p[n].val);
}

return 0;

}

细细分析,向实际思考,这种题条理就清晰了,算一个比较水的题。

posted on 2016-07-08 14:29  Greaterlee  阅读(140)  评论(0)    收藏  举报