3362 -- 【模拟试题】聪明的农民
Description
在城中有一个贪婪而愚蠢的领主,他总是不管农民的死活,疯狂地敲诈农民们辛勤耕种的劳动成果。眼看每年的秋收日十月十日已经到了,这时城中所有的农民在忙完一年的收成后,又要向领主上缴一年的粮食了。很久以来当地就采用一个特殊的“抽签”决定各户农民上缴粮食的方法:领主将一些写有不同的数的纸条放入一个盒子中,每户农民从中抽取两张纸条并在第二天上缴和两张纸条上的数值相乘结果同样数量的粮食。
城中的农民是个既聪明又团结的整体,他们非常痛恨残暴的领主,不希望向他多交一粒粮食,所以他们每年在抽完签后,秘密地交换一些纸条,使最后向领主上缴的粮食总量最少。不幸的是,以往决定如何交换纸条的老农夫杰克病倒了,不能为大家出力了,所以请你帮助他们决定如何交换纸条。另外,为了瞒过领主,活动只能在夜间悄悄进行,因此务必要使交换次数最少(两个农民将各自的一张纸条对调叫做一次交换)。
Input
文件的第一行为农户数目n(n≤500),接下来的n行,每行为两个数a、b,其中a、b两数均不超过1000。相邻两个数之间由一个或多个空格分隔。
Output
输出文件由两个数组成,每个数占一行,分别为交换后向领主上缴的粮食总量和需要交换的总次数。
Sample Input
3
10 13
7 12
9 6
Sample Output
252
1
第一个问是一个比较数学的问题,可以证明,大*小得到的乘积最小,这里我们肯定先贪心优化比较大的数,于是排序,两根指针,头*尾累加即可。
第二个问题可以根据第一个问的结果进行求解,进行每组枚举,如果“原本组”与“匹配组”的不同,就修改各自组别,sum++,输出即可。
yo,这里是代码。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
int n,ans1=0,ans2=0,a[1000005]={0};
int b[100005]={0};
int idx[100045]={0};
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int k,q;
scanf("%d%d",&k,&q);
a[i*2-1]=k;a[i*2]=q;
b[i*2-1]=k;b[i*2]=q;
idx[k]=idx[q]=i;
}
sort(a+1,a+2*n+1);
for(int i=1;i<=n;i++)ans1+=a[i]*a[2*n-i+1];//贪心
printf("%d\n",ans1);
for(int i=1;i<=n;i++)
{
if(idx[a[i]]!=idx[a[2*n-i+1]])
{
ans2++;
bool fl=0;
for(int j=1;j<=n;j++)
{
if(idx[b[j*2-1]]==idx[a[i]]&&(b[j*2-1]!=a[i]))
{
idx[b[2*j-1]]=idx[a[2*n-i+1]];
idx[a[2*n-i+1]]=idx[a[i]];
fl=1;
break;
}
}
if(fl)continue;
for(int j=1;j<=n;j++)
{
if(idx[b[2*j]]==idx[a[i]]&&(b[2*j]!=a[i]))
{
idx[b[2*j]]=idx[a[2*n-i+1]];
idx[a[2*n-i+1]]=idx[a[i]];
break;
}
}//交换各自组别
}
}
printf("%d\n",ans2);
return 0;
}