题意:
有n个小朋友竞选班长,1想当班长,每个人都必须选择一个人当班长,并且不可以选择自己,并且每个人都有一个权值ai,这个权值就是如果1想让这个人改变主意选择自己当班长就得给他ai个糖果,只有当1的票数是唯一最多的时候,1才能竞选班长,问1竞选班长的最小花费糖果数。(n<=100)
思路:
想了直接贪心但是似乎没有完美的方案,都会有反例。
考虑n<=100比较小,可以枚举+贪心。我第一次想的是枚举除1以外的人票数为i,如果其他人中有票数大于i的话,就贿赂投这个人的人,然后最后看1有多少票数,再根据会不会出现x,x-1,x-1…的情况分类讨论,就很麻烦,结果不对。对拍了一下发现这种做法本身就是错的,比如这一组例子,
8
3 4 3 2 4 2 7
35 13 19 2 70 98 7
其实只要13+2+7就可以竞选成功,但是如果用这种方法的话就大得多了。
正解的枚举是枚举1竞选成功的票数i,如果其他人中有票数大于i-1的话,就贿赂投这个人的人,最后如果1的票数大于i的话,不更新答案,等于i的话,更新答案,小于i的话,从还没贿赂的人里选最便宜的贿赂。但是1不是还要投一票吗?出现x,x-1,x-1…的情况那怎么办呢?其实这种情况是不可能出现的,因为x肯定大于2,总票数为x+(n-1)×(x-1)+1,2+(n-1)×1=n+2,但是总票数应该为n,相矛盾了,所以这种情况是不会出现的。
代码:
int f[110],c[110],num[110];
multiset<int>s[110];
multiset<int>ss;
int main()
{
int t,n;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
memset(num,0,sizeof(num));
for(int i=2;i<=n;i++)
{
scanf("%d",&f[i]);
num[f[i]]++;
s[i].clear();
}
for(int i=2;i<=n;i++)
{
scanf("%d",&c[i]);
if(f[i]!=1)s[f[i]].insert(c[i]);
}
int ans=INF;
for(int i=1;i<=n;i++)//1竞选时候的票数
{
ss.clear();
int temp=0;
int sum=num[1];//当前1的票数
for(int j=2;j<=n;j++)
{
if(num[j]>i-1)
{
multiset<int>::iterator it;
int need=num[j]-i+1;
for(it=s[j].begin();it!=s[j].end();it++)
{
if(need!=0){temp+=*it;need--;}
else ss.insert(*it);
}
sum+=num[j]-i+1;
}
else
{
if(!s[j].empty())
{
multiset<int>::iterator it;
for(it=s[j].begin();it!=s[j].end();it++)ss.insert(*it);
}
}
}
if(sum>i)continue;
else if(sum==i)ans=min(ans,temp);
else
{
int need=i-sum;
multiset<int>::iterator it;
for(it=ss.begin();it!=ss.end()&&need!=0;it++)
{
temp+=*it;
need--;
}
ans=min(ans,temp);
}
}
printf("%d\n",ans);
}
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!