思路:
- 利用并查集的思想,把每一位成员进行分类,同一个家庭的分在一起,并以每行第一个人为领导;
- 再进行暴力枚举,对每个家庭的信息进行处理。
- 其中关键是并查集的思想、sort函数快速排序的使用和printf函数控制输出
详细得在代码有解释~~
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<string>
#include<string.h>
#include<stdio.h>
using namespace std;
struct node//表示人员的节点
{
int hnum;//房产数量
int area;//房产面积
node() { hnum = area = 0; }
};
struct Ans//表示家庭的节点
{
int id, num;
double avehnum, avearea;
bool operator <(Ans &b)//家庭信息按人均面积降序输出,若有并列,则按成员编号升序输出
{
if (b.num*avearea == num * b.avearea)//本来是除法,变换一下换成乘法
return id < b.id;//从大到小a>b,从小到大a<b(用于下面的sort函数)
else
return b.num*avearea > num*b.avearea;
}
};
//bool cmp(Ans a, Ans b) {
//if (a.id != b.score)
//return a.id > b.id;//从大到小a>b,从小到大a<b
//else return strcmp(a.name, b.name) < 0;//strcmp是string.h头文件下用来比较两个char型字符串的字典序大小,
//str1小于str2返回负数
//}
node a[10010];//成员
int f[10010];//并查集记录
Ans ans[10010];//家庭
bool vis[10010], vv[10010];
//并查集的两个函数find、 merge
int Find(int x)
{
if (x == f[x])//x是它自己本身,说明他是这个集合的领导
return x;
else
{
f[x] = Find(f[x]);//深度遍历,找到最终的领导
return f[x];
}
}
void Merge(int x, int y)
{
x = Find(x);
y = Find(y);
if (x != y)
{
f[y] = x;//让x变成y的领导
}
}
void init()
{
memset(vis, 0, sizeof(vis));
memset(vis, 0, sizeof(vis));
for (int i = 0; i < 10000; i++)//初始化并查集
{
f[i] = i;
}
}
int main()
{
int n, k, zd, fd, md, cd;
cin >> n;
init();
for (int i = 0; i < n; i++)//将家庭成员合并在一起
{
cin >> zd >> fd >> md;//本人、爸爸、妈妈的编号
vis[zd] = 1;
if (fd != -1)
{
Merge(zd, fd);
vis[fd] = 1;
}
if (md != -1)
{
Merge(zd, md);
vis[md] = 1;
}
cin >> k;//孩子的数目
for (int j = 0; j < k; j++)
{
cin >> cd;
Merge(zd, cd), vis[cd] = 1;
}
cin >> a[zd].hnum >> a[zd].area;//房产套数和总面积
}
int top = 0;
for (int i = 0; i < 10000; i++)//求家庭信息
{
if (vis[i])
{
int x = Find(i);
if (vv[x])//领导者被初始化过的情况
{
for (int j = 0; j < top; j++)//遍历已有家庭
{
if (Find(ans[j].id) == x)//找到对应的家庭
{
ans[j].num++;
ans[j].avearea += a[i].area;
ans[j].avehnum += a[i].hnum;
break;//找到就退出,减少时间复杂度
}
}
}
else//领导者没有初始化过的情况
{
vv[x] = 1;
ans[top].id = i;//领导者编号
ans[top].num = 1;
ans[top].avearea = ans[top].avehnum = 0;
ans[top].avearea += a[i].area;
ans[top].avehnum += a[i].hnum;
top++;
}
}
}
sort(ans, ans + top);//默认以结构体第一行排序,升序;
//!!但是,由于上面的ans结构体对符号<进行了重载,所以排序结果由上面的函数决定
cout << top << endl;
for (int i = 0; i < top; i++)
{
/*cout << ans[i].id << " " << ans[i].num << " ";
cout << fixed << setprecision(3) << (double)ans[i].avehnum / ans[i].num << " ";
cout << fixed << setprecision(3) << (double)ans[i].avearea / ans[i].num << endl;*/
printf("%.04d %d %.3lf %.3lf\n", ans[i].id, ans[i].num, 1.0*ans[i].avehnum / ans[i].num, 1.0*ans[i].avearea / ans[i].num);
}
system("pause");
return 0;
}
附上printf()的格式输出
对于C语言的输出格式("/n%2d,%2.1f,%2.11f)是什么意思
#include "stdio.h"
int main()
{
int a=1234;
float b=123.456;
double c=12345.54321;
printf("/n%2d,%2.1f,%2.11f",a,b,c);
}
输出结果为1234,123.5,12345.5
%md: m为指定输出数据的宽度 如果位数小于m,则左端补以空格,如果大于m,则按实际位数输出
显然a=1234的位数是4 大于%2d中指定的长度2 按实践位数输出 所以结果为1234
%m.nf格式:指定输出数据宽度为m位,其中小数占n位,如果数值长度小于m,则左端补空格,%-m.nf与上差不多,只是如果 数值长度小于m,则右端补空格,大于的话就按原长度输出
%2.1f就是指长度2位,含一位小数位,由于原长度大于2,所以原样输出,但只能右一位小数,所以结果是123.4
%2.11f,就是含11位小数,显然长度不够,则右端补零 结果位12345.54321000000
请教%.2f与%1.2f有什么区别
1.2f是非法的,小数点前的数字必须大于小数点后的数字。小数点前的数值规定了打印的数字的总宽度。如果忽略了(也就是.2f),总宽度无限制。
%.lf(四舍五入)
C语言程序中浮点数类型(%.2lf)编译器默认四舍五入,(最好自行测试一遍,可能不同平台使用的C语言版本不同,语言标准也有细微的区别。)如果不需要四舍五入,则要自行处理(浮点数-5*10^(-x),x=需要保留的小数位+1)。(已经写过测试代码进行了验证!!!)。另外,float精度是2^23,能保证6位,double的精度是2^52,能保证15位。double精度的确有那么高,但是默认打印位数没有那么多。
%lf和%f那些事
1、%lf和%f 在scanf函数和printf函数看来是不一样的
首先printf无论是%f还是%lf没有区别,因为当printf函数当遇到float类型时会自动转化为double,从c语言标准来说printf并没有%lf的定义,虽然大多数编译器都能接受,但在做题时printf最好用%f,否则可能出现一些莫名其妙的错误;
对于scanf来说double就应该用%lf float就用%f
C语言中%d %.2d %2d %02d的区别
%d:即为普通的输出
%2d:按宽度为2输出,右对齐方式输出。若不够两位,左边补空格。
%02d:同样宽度为2,右对齐方式。位数不够,左边补0。
%.2d:从执行效果来看,与%02d一样。
求赞~~