PTA L2-007 家庭房產 (25 分) (詳解)


給定每個人的家庭成員和其自己名下的房產,請你統計出每個家庭的人口數、人均房產面積及房產套數。

輸入格式:

輸入第一行給出一個正整數N(1000),隨后N行,每行按下列格式給出一個人的房產:

編號 父 母 k 孩子1 ... 孩子k 房產套數 總面積
 

其中編號是每個人獨有的一個4位數的編號;分別是該編號對應的這個人的父母的編號(如果已經過世,則顯示-1);k0k5)是該人的子女的個數;孩子i是其子女的編號。

輸出格式:

首先在第一行輸出家庭個數(所有有親屬關系的人都屬於同一個家庭)。隨后按下列格式輸出每個家庭的信息:

家庭成員的最小編號 家庭人口數 人均房產套數 人均房產面積
 

其中人均值要求保留小數點后3位。家庭信息首先按人均面積降序輸出,若有並列,則按成員編號的升序輸出。

 

 

輸入樣例:

10
6666 5551 5552 1 7777 1 100
1234 5678 9012 1 0002 2 300
8888 -1 -1 0 1 1000
2468 0001 0004 1 2222 1 500
7777 6666 -1 0 2 300
3721 -1 -1 1 2333 2 150
9012 -1 -1 3 1236 1235 1234 1 100
1235 5678 9012 0 1 50
2222 1236 2468 2 6661 6662 1 300
2333 -1 3721 3 6661 6662 6663 1 100

輸出樣例:

3
8888 1 1.000 1000.000
0001 15 0.600 100.000
5551 4 0.750 100.000

----------------------

這個題目可以用兩種方法實現

第一種:把它當成圖來處理,廣搜或深搜一遍就可以了,對圖的遍歷比較熟悉的廣搜和深搜應該不是什么難點,那主要是看建圖了,理論上以何種方式建圖都可以達到目的選擇最方便的那個,對於節點序號不是像0 1 2 3....那簡單的圖就需要一點技巧了,建一個很大的數組用來存編號,直接用map映射到0 1 2 3....(這樣就得考慮后續是否還需要通過下標0 1 2 3...找到對應編號,因為map並不是雙向映射,從左到右好找,從右往左就不好找了,或許可以用兩個map來實現雙向映射),不過我用的解法是建一個很大的數組(這應該是通常用的方法吧)。。。。不過用建圖的方式寫的答案只能得22分最后有一個測試點沒過,我也沒去深究其錯在哪了。。如果哪位好奇寶寶發現我哪里還少考慮了可以留言告訴我,非常感謝!

代碼如下

//鏈式前向星,無向圖,雙向建邊
//或許很多數組可以用vector來節省空間,不過無所謂了
#include <bits/stdc++.h>
using namespace std;
vector<int> head(10000, -1); //所有人,下標對應於編號
set<int> All;                //記錄所有人的編號,用於處理單個點的情況
struct Room
{
    int num;   //房產數量
    int S;     //總面積
} room[10000]; //對應每個編號人的房產情況

struct Edge
{
    int to;
    int next;
} edge[10050];
int ep = 0; //邊數

struct answer
{
    int midid = 9999; //最小編號
    int num;          //人數
    double rooms;     //房產數
    double S;         //面積
} res[1005];          //存儲結果的數組
int rp = 0;           //家庭個數
int vis[10050];       //已訪問

void dfs(int to)
{
    if (All.count(to))
        All.erase(to);
    //其實這一步是剛開始沒考慮好的緣故,發現答案不對才加的這一步(滑稽)
    //深搜中所有出現過的點至少有兩個點連在一起的,因為判斷終止條件就是指針是否指到-1,
    //而head默認值就是-1,如果某個節點它沒有連接到其他點那么它head值就是-1,
    //此時判斷到這里的時候就會直接跳過,所有用set來保存所有節點訪問到的就刪除,留下的就全是未與其他點相連的點了

    int p = head[to]; //指針,用來操作鄰邊
    //res都是用來記錄結果的
    res[rp].num++;
    res[rp].midid = min(to, res[rp].midid);
    res[rp].rooms += room[to].num;
    res[rp].S += room[to].S;
    while (p != -1) //循環每個連通部分,訪問之后vis數組置1
    {
        int to = edge[p].to;
        if (vis[to] == 0)
        {
            vis[to] = 1;
            dfs(to);
        }
        p = edge[p].next;
    }
}
//排序比較函數
bool cmp(answer &a1, answer &a2)
{
    if (a1.S / a1.num == a2.S / a2.num)
        return a1.midid < a2.midid;
    return a1.S / a1.num > a2.S / a2.num;
}

void test()
{
    int n; //總人數
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        int t1, t2, t3, t4; //分別表示編號 本人 父 母 孩子
        int kidnum;         //孩子個數
        cin >> t1 >> t2 >> t3 >> kidnum;
        All.insert(t1); //存儲主體
        //父母建邊
        if (t2 != -1)
        {
            edge[ep].to = t2;
            edge[ep].next = head[t1];
            head[t1] = ep++;
            //雙向
            edge[ep].to = t1;
            edge[ep].next = head[t2];
            head[t2] = ep++;
        }
        if (t3 != -1)
        {
            edge[ep].to = t3;
            edge[ep].next = head[t1];
            head[t1] = ep++;
            //雙向
            edge[ep].to = t1;
            edge[ep].next = head[t3];
            head[t3] = ep++;
        }
        //孩子建邊
        for (int j = 1; j <= kidnum; j++)
        {
            cin >> t4;
            edge[ep].to = t4;
            edge[ep].next = head[t1];
            head[t1] = ep++;
            //雙向
            edge[ep].to = t1;
            edge[ep].next = head[t4];
            head[t4] = ep++;
        }
        cin >> room[t1].num >> room[t1].S;
    }
    //循環遍歷
    for (int i = 0; i < 10000; i++)
    {
        if (head[i] != -1 && vis[i] == 0)
        {
            vis[i] = 1;
            dfs(i);
            rp++;
        }
    }
    //單點情況處理
    if (!All.empty()) //如果set中不為空說明有點未與其他點相連
    {
        for (int i : All)
        {
            res[rp].num++;
            res[rp].S = room[i].S;
            res[rp].midid = i;
            res[rp].rooms = room[i].num;
            rp++;
        }
    }
    //排序
    sort(res, res + rp, cmp);

    //答案輸出
    cout << rp << endl;
    for (int i = 0; i < rp; i++)
    {
        printf("%04d %d %.3lf %.3lf\n", res[i].midid, res[i].num, res[i].rooms / res[i].num, res[i].S / res[i].num);
    }
}

int main()
{
    test();
    return 0;
}

 

第二種:並查集,用並查集的好處是題目中要求找出最小的下標,並查集需要用一個特征來表示唯一的祖先(最大、最小.....,實際沒有這個特征其實也可以用,似乎說了跟沒說一樣哈哈)

這種解法就比上一種解法方便許多,主要需要  fa數組表示各編號的祖先,一個set集合用來記錄所有出現過的人的編號,然后就是一個夠大的數組記錄房產信息(這里其實可以完全不用數組,用一個map編號直接映射房產信息的結構體就完了,這更節省空間),用並查集寫很方便的地方在於不用創建很大的數組(除了fa數組),還有就是只需要遍歷所有編號就能得出所有結果了,遍歷的順序對結果沒有任何影響,因為只要是一個家族里面的,其祖先就是唯一的(最小的那個下標)

代碼如下

//並查集
#include <bits/stdc++.h>
using namespace std;
int fa[10050]; //記錄祖先
set<int> peo;  //記錄所有人的編號,在輸入的時候記錄就行了,有的編號輸入時出現不止一次所有用set確保唯一
struct people  //房產信息,完全可以用map來代替head數組
{
    int rooms;
    double S;
} head[10050];
struct answer //結果,一個家族的全部信息,用最小編號映射家族信息
{
    int minid;
    int num;
    int rooms;
    double S;
}; //家庭個數
map<int, answer> mp;
void init() //fa數組初始化
{
    for (int i = 0; i < 10050; i++)
        fa[i] = i;
}

int Find(int a) //找祖先
{
    if (fa[a] == a)
        return a;
    else
        return fa[a] = Find(fa[a]);
}

void Union(int a, int b) //合並祖先
{
    int f1 = Find(a);
    int f2 = Find(b);
    if (f1 < f2)     //最小的為祖先
        fa[f2] = f1; //修改的是祖先的祖先,祖先的祖先就是其本身
    else
        fa[f1] = f2;
}
//排序比較函數
bool cmp(answer &a1, answer &a2)
{
    if (a1.S / a1.num == a2.S / a2.num)
        return a1.minid < a2.minid;
    return a1.S / a1.num > a2.S / a2.num;
}

void test()
{
    init();
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        int id, p1, p2, knum, k;//分別代表 本人 父 母 孩子數量 孩子
        cin >> id >> p1 >> p2 >> knum;
        peo.insert(id);//記錄所有編號
        if (p1 != -1)
        {
            peo.insert(p1);//記錄所有編號
            Union(id, p1);
        }
        if (p2 != -1)
        {
            peo.insert(p2);//記錄所有編號
            Union(id, p2);
        }
        for (int j = 0; j < knum; j++)
        {
            cin >> k;
            peo.insert(k);//記錄所有編號
            Union(id, k);
        }
        cin >> head[id].rooms >> head[id].S;
    }

    for (int i : peo)//遍歷set,只需要得到編號即可,mp中保存的是最后結果
    {
        int id = Find(i);
        mp[id].minid = id;
        mp[id].num++;
        mp[id].S += head[i].S;
        mp[id].rooms += head[i].rooms;
    }
    cout << mp.size() << endl;
    vector<answer> res;//轉到數組中對其進行排序輸出
    for (auto i : mp)
    {
        res.push_back(i.second);
    }
    //排序
    sort(res.begin(), res.end(), cmp);
    //輸出結果
    for (auto i : res)
        printf("%04d %d %.3lf %.3lf\n", i.minid, i.num, 1.0 * i.rooms / i.num, 1.0 * i.S / i.num);
}

int main()
{
    test();
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM