PAT天梯賽Level2題目題解彙總

L2-001 緊急救援

/*******************************************************************************
Date: 2018/2/2
Author: Wen Yaxin

題目簡介:需要求S到D最短路徑的條數,多條最短路徑的條件上,
需要輸出救援人數最多的那條。輸出最大的救援人數,並把
相應的路徑輸出。

解題思路:好題,最短路變形,首先求最短路,如果最短路徑
相同,則看經過另一個點能否增多救援人數,可以的化,照樣
更新對應的路徑。

代碼變量含義:
Map : 圖
path:存放路徑,path[i]存放節點i的前驅。
vis :標記節點是否已經求出最短路徑,vis[i]=1已求出最短路
pathNum:pathNum[i]是源點S到頂點i最短路徑的條數
num :存放每個節點的救援人數
dist:存放源點到每個節點的最短路徑長度。
people:存放源點到當前節點的路徑中所有救援人員的總人數
*********************************************************************************/

#include <stdio.h>
#include <iostream>
#include <stack>
using namespace std;

const int maxn = 502;
const int inf = 0x3f3f3f3f;
int N,M,S,D;
int Map[maxn][maxn];
int path[maxn],vis[maxn],pathNum[maxn];
int num[maxn],dist[maxn],people[maxn];  //每個城市救援隊的數目
void initMap() {
    for(int i = 0; i < N; i++) {
        for(int j = 0; j < N; j++) {
            if(i == j) Map[i][j] = 0;
            else Map[i][j] = inf;
        }
    }
}
void dijkstra() {
    people[S] = num[S];
    vis[S] = 1;
    dist[S] = 0;
    pathNum[S] = 1;
    path[S] = -1;
    for(int i = 0; i < N; i++) {
        dist[i] = Map[S][i];
        if(Map[S][i]<inf && i != S) {
            people[i] = num[i]+num[S];
            pathNum[i] = 1;
            path[i] = S;
        }
    }
    int u,mindis;
    for(int i = 1; i < N; i++) {
        mindis = inf;
        u = S;
        for(int j = 0; j < N; j++) {
            if(vis[j]==0 && dist[j]<mindis) {
                u = j;
                mindis = dist[j];
            }
        }
        vis[u] = 1;
        for(int j = 0; j < N; j++) {
            if(vis[j]==0 && Map[u][j]<inf) {
                if(dist[u]+Map[u][j]<dist[j]) {
                    pathNum[j] = pathNum[u];
                    path[j] = u;
                    dist[j] = dist[u] + Map[u][j];
                    people[j] = people[u] + num[j];
                }
                else if(dist[u]+Map[u][j] == dist[j]) {
                    pathNum[j] += pathNum[u];
                    if(people[u]+num[j]>people[j]) {
                        people[j] = people[u] + num[j];
                        path[j] = u;
                    }
                }
            }
        }
    }
}
void printAns() {
    printf("%d %d\n",pathNum[D],people[D]);
    stack<int>st;
    st.push(D);
    while(path[D] != -1) {
        D = path[D];
        st.push(D);
    }
    printf("%d",st.top());
    st.pop();
    while(!st.empty()) {
        printf(" %d",st.top());
        st.pop();
    }
    printf("\n");
}
int main() {
    while(~scanf("%d%d%d%d",&N,&M,&S,&D)) {
        initMap();
        for(int i = 0; i < N; i++) {
            scanf("%d",&num[i]);
        }
        int u,v,len;
        for(int i = 0; i < M; i++) {
            scanf("%d%d%d",&u,&v,&len);
            if(len < Map[u][v]) {
                Map[u][v] = Map[v][u] = len;
            }
        }
        dijkstra();
        printAns();
    }
    return 0;
}

L2-002 鏈表去重

/**********************************************************************************
Date: 2018/2/2 20:57
Author: Wen Yaxin

題目簡介:將鏈表中絕對值重複的元素去除,原鏈表形成一個新
鏈表,被刪除的元素組成一個新鏈表,先輸出去重後的鏈表,再
輸出被去重的元素組成的鏈表。注意鏈表中刪除元素後,其下
一個指向會變。

解題思路:對於一個節點維護本身的值,然後存放下一個元素的
值,然後從表頭開始遍歷鏈表,如果其節點值的絕對值沒有出現
過,將該節點的地址放入數組1,並標記該節點的值,否則將地址
放入節點2。

代碼變量含義:
vis :標記數組,用來標記絕對值爲i的值有沒有出現過
arr1:存放去重後的鏈表的節點所在的地址。
arr2:存放被刪除的節點所在的地址
node:維護一個個鏈表節點
*************************************************************************************/
#include <stdio.h>
#include <iostream>
#include <cmath>
#include <string.h>

using namespace std;

const int maxn = 1e5+10;
int vis[maxn],arr1[maxn],arr2[maxn];
struct Node {
    int value;  //值
    int nex;    //下個節點的座標
}node[maxn];
int main() {
    int start,address,key,nex,num;
    while(~scanf("%d%d",&start,&num)) {
        memset(vis,0,sizeof(vis));
        for(int i = 0; i < maxn; i++) {
            node[i].nex = -1;
        }
        for(int i = 0; i < num; i++) {
            scanf("%d%d%d",&address,&key,&nex);
            node[address].value = key;
            node[address].nex = nex;
        }
        int cnt1=0,cnt2=0;
        while(start != -1) {
            key = node[start].value;
            int temp = abs(key);
            if(vis[temp] == 0) {
                vis[temp] = 1;
                arr1[cnt1++] = start;
            }
            else {
                arr2[cnt2++] = start;
            }
            start = node[start].nex;
        }
        for(int i = 0; i < cnt1; i++) {
            int temp = arr1[i];
            if(i != cnt1-1)
                printf("%05d %d %05d\n",temp,node[temp].value,arr1[i+1]);
            else
                printf("%05d %d -1\n",temp,node[temp].value);
        }
        for(int i = 0; i < cnt2; i++) {
            int temp = arr2[i];
            if(i != cnt2-1)
                printf("%05d %d %05d\n",temp,node[temp].value,arr2[i+1]);
            else
                printf("%05d %d -1\n",temp,node[temp].value);
        }

    }
    return 0;
}

L2-003 月餅

/*****************************************************
Date: 2018/2/4 20:16
Author: Wen Yaxin

解題思路:求出每種月餅的單價,按照單價從大到小排序。
然後從貴的月餅開始買即可。

變量含義:
node:維護每種的月餅的總重,總價和單價。
******************************************************/
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>

using namespace std;

struct Node {
    double weight;   //總庫存量
    double allMoney; //總價格
    double price; //單價
}node[1002];
bool cmp(Node a,Node b) {
    return a.price>b.price;
}
int main() {
    int n,d;
    while(~scanf("%d%d",&n,&d)) {
        for(int i = 0; i < n; i++) {
            scanf("%lf",&node[i].weight);
        }
        for(int i = 0; i < n; i++) {
            scanf("%lf",&node[i].allMoney);
            node[i].price = node[i].allMoney/node[i].weight;
        }
        sort(node,node+n,cmp);
        double sum = 0;
        for(int i = 0; i < n; i++) {
            if(d >= node[i].weight) {
                sum = sum + node[i].allMoney;
                d = d - node[i].weight;
            }
            else {
                sum = sum + d*node[i].price;
                break;
            }
        }
        printf("%.2lf\n",sum);
    }
    return 0;
}
L2-004 這是二叉搜索樹嗎?
/************************************************************************
Date: 2018/2/4 21:19
Author: Wen Yaxin

題目簡介:給出一個序列,判斷該樹是否是二叉搜索樹或其鏡像樹的
前序序列。如果是輸出YES和該樹的後續序列,否則輸出NO。

解題思路:該題採用逆向思維,如果熟知二叉搜索樹的建樹法則,
變可對序列進行建樹,首先對給出的序列建立二叉搜索樹以及其鏡像
樹,然後對這兩顆樹進行先序遍歷,如果遍歷出的序列和題目中給出
的序列相同,則說明題目給出的序列的確是二叉搜索樹的先序序列。
否則說明題目給出的序列不是二叉搜索樹的先序序列。然後對樹求
後續。

變量含義:
node: 二叉搜索樹節點
node->key:節點對應的值
node->lchild:左孩子節點
node->rchild:右孩子節點
**************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stdlib.h>
#include<stack>

using namespace std;

int a[100000],b[1000000],n,flag;
typedef struct node
{
  int key;
  struct node *lchild,*rchild;
}BSTNode;
//根據序列構建正常樹
int InsertBST1(BSTNode *&p,int k)
{
  if(p == NULL)
  {
    p = (BSTNode*)malloc(sizeof(BSTNode));
    p->key = k;
    p->lchild = p->rchild = NULL;
    return 1;
  }
  else if(k < p->key)
    return InsertBST1(p->lchild,k);
  else
    return InsertBST1(p->rchild,k);
}
//根據序列構建鏡面樹
int InsertBST2(BSTNode *&p,int k)
{
  if(p == NULL)
  {
    p = (BSTNode*)malloc(sizeof(BSTNode));
    p->key = k;
    p->lchild = p->rchild = NULL;
    return 1;
  }
  else if(k < p->key)
    return InsertBST2(p->rchild,k);
  else
    return InsertBST2(p->lchild,k);
}
//求樹的先序序列
void PreOrder(BSTNode *bt)
{
  stack<BSTNode*>st;
  BSTNode *p;
  int k = 0;
  if(bt!=NULL)
  {
    st.push(bt);
    while(!st.empty())
    {
      p = st.top();
      st.pop();
      b[k++] = p->key;
      if(p->rchild!=NULL)
        st.push(p->rchild);
      if(p->lchild!=NULL)
        st.push(p->lchild);
    }
  }
}
//求樹的後續序列
void PostOrder(BSTNode *bt)
{
  if(bt!=NULL)
  {
    PostOrder(bt->lchild);
    PostOrder(bt->rchild);
    if(flag)
    {
      printf("%d",bt->key);
      flag = 0;
    }
    else
      printf(" %d",bt->key);
  }
}
//判斷給出的序列是否是正常樹或鏡面樹的先序序列
void IsRight(BSTNode *bt1,BSTNode *bt2)
{
  int state1 = 1,state2 = 1,i;
  flag = 1;
  PreOrder(bt1); //對正常樹進行先序遍歷
  for(i = 0; i < n; i++)
    if(a[i]!=b[i])
    {
      state1 = 0;
      break;
    }
  if(state1)
  {
    printf("YES\n");
    PostOrder(bt1);
    printf("\n");
    return;
  }
  flag = 1;
  PreOrder(bt2);
  for(i = 0; i < n; i++)
    if(a[i]!=b[i])
    {
      state2 = 0;
      break;
    }
  if(state2)
  {
    printf("YES\n");
    PostOrder(bt2);
    printf("\n");
    return;
  }
  printf("NO\n");
  return;
}
int main()
{
  int i;
  while(~scanf("%d",&n))
  {
    BSTNode *bt1 = NULL;  //正常樹樹根
    BSTNode *bt2 = NULL;  //鏡面樹樹根
    for(i = 0; i < n; i++)
    {
      scanf("%d",&a[i]);
      InsertBST1(bt1,a[i]);  //構建正常樹
      InsertBST2(bt2,a[i]);  //構建鏡面樹
    }
    IsRight(bt1,bt2);
  }
  return 0;
}
L2-005 集合相似度
/*****************************************************
Date: 2018/2/4 21:39
Author: Wen Yaxin

解題思路:用stl中的set來解思路很簡單,提問集合 s1和s2的相似度,
本來的想法是將s1中的元素放入set1中,將s2中的元素放入set2中,
這樣調用size函數就可以求出s1中不同元素的個數,以及s2中不同元素
的個數,然後再將s1和s2放到一個set3裏面,是兩個集合中不同元素的
個數。因此:NC = set1.size()+set2.size()-set3.size();
NT = set3.size();
******************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#define INF 1000
#define max(a,b) a>b? a:b

using namespace std;

set<int>Set[100002];
int main()
{
    int N,M,K;
    int s1,s2,temp;
    while(~scanf("%d",&N))
    {
        for(int i = 1; i <= N; i++)
        {
            scanf("%d",&M);
            if(!Set[i].empty())  // 如果第i個集合非空,清空
                Set[i].clear();
            for(int j = 1; j <= M; j++)
            {
                scanf("%d",&temp);
                Set[i].insert(temp);  //將temp插入到集合i
            }
        }
        scanf("%d",&K);  //接下來會詢問K個集合的相似度
        while(K--)
        {
            scanf("%d%d",&s1,&s2);  //求集合1和集合2的相似度
            set<int>::iterator it;
            int count = 0;          //用來存放兩個集合都有的不同元素的個數
            for(it = Set[s1].begin(); it != Set[s1].end(); it++)
            {
                if(Set[s2].count(*it))  //如果*it在集合2中
                    count++;
            }
            int nt = Set[s1].size()+Set[s2].size()-count;
            double ans = (count*1.0)/nt*100;
            printf("%.2lf%%\n",ans);
        }
    }
    return 0;
}

L2-006 樹的遍歷

/**************************************************************************************
Date: 2018/2/5 18:59
Author: Wen Yaxin

解題思路:對於二叉樹,如果只給出先序遍歷序列,中序遍歷序列或後續遍歷
序列,是不能確定二叉樹的樹形的,在李春保的數據結構書的樹這一數據結構
這一章畫有若干圖表明瞭這一結論,但是給出先序+中序或給出後序+中序可以
確定二叉樹的形狀,因此對於本題給出的後序和中序是可以確定唯一一顆二叉
樹的,對於後序,其遍歷順序是左右根,因此可知給出的序列中的最後一個元素
便是根節點,根據該根節點在中序中查找到該節點,由於中序的遍歷順序是左根
右,因此節點左側序列是其左子樹的中序遍歷,右側是其右子樹的中序遍歷。

變量解釋:
post:後序序列
in:中序序列
node:二叉樹節點
*p:輔助指針,在查找in數組的時候,指向查找到的位置
*b:建立二叉樹節點用的輔助指針
r:當前後序序列的最後一個值,即當前後序、中序對應的根節點。
**************************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stdlib.h>
#include<stack>
#include<set>
#include<queue>

using namespace std;

int n;
int post[50],in[50];
typedef struct node
{
  int data;
  struct node *lchild;
  struct node *rchild;
}BTNode;
BTNode* CreateTree(int *post,int *in,int n)
{
  BTNode *b;         //建立節點用的輔助指針
  int r,*p;
  int k;
  if(n <= 0) return NULL;
  r = *(post+n-1);   //初始指向最後後序最後一個元素
  b = (BTNode*)malloc(sizeof(BTNode));
  b->data = r;
  //在中序中查找該元素。
  for(p = in; p < in+n; p++)
  {
    if(*p == r) break;
  }
  k = p-in;   //找到中序中對應元素的下標
  //in數組k左側都屬於當前接待你的左子樹
  b->lchild = CreateTree(post,in,k);
  //post+k後和p+1後分別是當前右字數的後序和中序。
  b->rchild = CreateTree(post+k,p+1,n-k-1);
  return b;
}
void LevelOrder(BTNode *b)
{
  queue<BTNode*>qu;
  BTNode *p;
  p = b;
  if(p!=NULL)  //if it isn't a empty tree
  {
    qu.push(p);  //first put the root into queue
  }
  int flag = 1;
  while(!qu.empty())
  {
    p = qu.front();
    qu.pop();
    if(flag)
    {
      printf("%d",p->data);
      flag = 0;
    }
    else
      printf(" %d",p->data);
    if(p->lchild!=NULL)
      qu.push(p->lchild);
    if(p->rchild!=NULL)
      qu.push(p->rchild);
  }
  printf("\n");
}
int main()
{
  while(~scanf("%d",&n))
  {
    for(int i = 0; i < n; i++)
      scanf("%d",&post[i]);
    for(int i = 0; i < n; i++)
      scanf("%d",&in[i]);
    BTNode *bt;
    bt = CreateTree(post,in,n);
    LevelOrder(bt);
  }
  return 0;
}
L2-007 家庭房產
/*******************************************************************************************
Date: 2018/2/5 21:00
Author: Wen Yaxin

題目簡述:給出N,然後給出N個人的房產情況。數據組織情況如下。
個人編號,父親編號,母親編號,這個人的孩子個數,
然後給出這個人孩子的編號,然後給出個人的房產套數,這些房產的總面積。
父親母親編號爲-1代表父親母親過世。

解題思路:並查集加排序。每一個人的編號維護一個集合,對於給出的一組
數據,涉及到的所有人員的編號都必須進行集合合併操作,集合合併的準則
是以編號小的人集合爲準,因爲答案要的家族代表是該家族中的最小編號,
對於每一個集合,我們需要維護該集合中的人數,該集合的房子套數,該集合
的房子總面積,通過查找元素所屬集合,假如查找出兩個元素分別在x集合,y集合
如果x和y相等,不需要合併,否則合併到編號較小的集合中,並且把另一個集合
的所有資源也都合並過去。由於給出的人員編號不連續,因此用vis數組進行標記,
將題目中所出現的所有編號都存儲起來(vis標記是爲了讓編號不被重複存儲),
然後通過這些編號去查找有多少集合,並把這些家庭存儲起來進行排序。最後輸出。

變量解釋:
N:代表將給出N個人的房產情況
f: f[i]中存放編號爲i的人所在的集合編號
houseNum:houseNum[i]代表以i爲編號的家族的房產數
houseArea:houseArea[i]代表以i爲編號的家族的房產總面積
people:people[i]代表以i爲編號的家族的人數
vis:vis[i]用來標記編號爲i的人有沒有被放入容器中
vector:v用來存儲題目中出現過的人員編號
***********************************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stdlib.h>
#include<stack>
#include<set>
#include<vector>

using namespace std;

const int maxn = 1e5+10;
int N,f[maxn],houseNum[maxn],houseArea[maxn],people[maxn];
int vis[maxn];
vector<int>v;
struct Node {
    int index;       //編號
    int familys;     //家人個數
    double averNum;  //人均房產套數
    double averArea; //人均房產面積
}node[maxn];
//初始化集合
void initSet() {
    for(int i = 0; i < maxn; i++) {
        f[i] = i;
        houseNum[i] = 0;
        houseArea[i] = 0;
        people[i] = 1;
    }
}
//查找某個人所在的家族編號
int findSet(int x) {
    while(x != f[x]) {
        x = f[x];
    }
    return x;
}
//合併集合,規則是往編號小的集合合併
void unionSet(int x,int y) {
    if(x != y) {
        //併到x
        if(x < y) {
            f[y] = x;
            people[x] += people[y];
            houseNum[x] += houseNum[y];
            houseArea[x] += houseArea[y];
        } //集合併到y
        else {
            f[x] = y;
            people[y] += people[x];
            houseNum[y] += houseNum[x];
            houseArea[y] += houseArea[x];
        }
    }
}
//對每組數據進行處理
void handle() {
    int my,father,monther,childNum,num,area,childId;
    int s1,s2,s3;
    scanf("%d%d%d%d",&my,&father,&monther,&childNum);
    if(!vis[my]) {
        v.push_back(my);
        vis[my] = 1;
    }
    if(father != -1) {
        if(!vis[father]) {
            v.push_back(father);
            vis[father] = 1;
        }
    }
    if(monther != -1) {
        if(!vis[monther]) {
            v.push_back(monther);
            vis[monther] = 1;
        }
    }
    s1 = findSet(my);
    if(father != -1) {
        s2 = findSet(father);
        unionSet(s1,s2);
    }
    s1 = findSet(my);
    if(monther != -1) {
        s3 = findSet(monther);
        unionSet(s1,s3);
    }
    while(childNum--) {
        scanf("%d",&childId);
        if(!vis[childId]) {
            v.push_back(childId);
            vis[childId] = 1;
        }
        s1 = findSet(my);
        s2 = findSet(childId);
        unionSet(s1,s2);
    }
    s1 = findSet(my);
    scanf("%d%d",&num,&area);
    houseNum[s1] += num;
    houseArea[s1] += area;
}
//排序用的構造函數
bool cmp(Node a,Node b) {
    //人均面積相等,按照編號對其進行升序排列
    if(fabs(a.averArea-b.averArea)<1e-4) {
        return a.index<b.index;
    }
    else {
        return a.averArea>b.averArea;
    }
}
int main() {

    while(~scanf("%d",&N)) {
        v.clear();  //清空vector
        initSet();  //集合初始化
        memset(vis,0,sizeof(vis));
        while(N--) {
            handle();
        }
        int cnt = 0;
        for(int i = 0; i < (int)v.size(); i++) {
            int id = v[i];
            if(f[id] == id) {
                node[cnt].index = id;
                node[cnt].familys = people[id];
                node[cnt].averNum = (houseNum[id]*1.0)/people[id];
                node[cnt++].averArea = (houseArea[id]*1.0)/people[id];
            }
        }
        sort(node,node+cnt,cmp);
        printf("%d\n",cnt);
        for(int i = 0; i < cnt; i++) {
            printf("%04d %d %.3lf %.3lf\n",node[i].index,node[i].familys,node[i].averNum,node[i].averArea);
        }
    }
    return 0;
}

L2-008 最長對稱子串

/***************************************************************************************
Date: 2018/3/10 9:25
Author: Wen Yaxin

解題思路:對每個字符進行枚舉,分別考慮它作爲奇數串中心的字符,和偶數串中心
的字符,然後從該字符向左向右擴展。尋找最長對稱串。

變量含義:
str:存放題目給出的字符串。
*******************************************************************************************/
#include <iostream>
#include <stdio.h>
#include <string.h>

using namespace std;

char str[1005];
int main() {
    while(gets(str)) {
        int len = strlen(str);
        int maxLen = 0,j;
        for(int i = 0; i < len; i++) {
            //求以i爲中心的奇數串。
            j = 0;
            while(i-j-1>=0 && i+j+1<len) {
                if(str[i-j-1] == str[i+j+1]) {
                    j++;
                }
                else {
                    maxLen = max(maxLen,2*j+1);
                    break;
                }
            }
            maxLen = max(maxLen,2*j+1);
            while(i-j-1>=0 && i+j<len) {
                if(str[i-j-1] == str[i+j]) {
                    j++;
                }
                else {
                    maxLen = max(maxLen,2*j);
                    break;
                }
            }
            maxLen = max(maxLen,2*j);
        }
        printf("%d\n",maxLen);
    }
    return 0;
}

L2-009 搶紅包

/**************************************************************************
Date: 2018/3/10 10:21
Author: Wen Yaxin

解題思路:一個人收益等於它搶到的錢-它發出去的錢。對於某個人發的紅包,
使用數組標記防止搶過紅包的人重複領,最後排序輸出。

變量含義:
vis:用來對搶過某個人紅包的人做標記
node:維護人的編號,搶紅包的收益,搶到紅包的數量。

方法含義:
init:對結構體數組進行初始化
cmp:排序所使用的構造函數
***************************************************************************/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;

const int maxn = 1e4+5;
int vis[maxn];
struct Node {
    int index;
    int num;
    double money;
}node[maxn];
void init(int n) {
    for(int i = 1; i <= n; i++) {
        node[i].index = i;
        node[i].money = 0;
        node[i].num = 0;
    }
}
bool cmp(Node a,Node b) {
    if(a.money == b.money) {
        if(a.num == b.num) {
            return a.index < b.index;
        }
        else {
            return a.num > b.num;
        }
    }
    else {
        return a.money > b.money;
    }
}
int main() {
    int N,K,M,P;
    while(~scanf("%d",&N)) {
        init(N);
        for(int i = 1; i <= N; i++) {
            scanf("%d",&K);
            memset(vis,0,sizeof(vis));
            while(K--) {
                scanf("%d%d",&M,&P);
                if(!vis[M]) {
                    vis[M] = 1;
                    node[M].money += P;
                    node[i].money -= P;
                    node[M].num++;
                }
            }
        }
        sort(node+1,node+1+N,cmp);
        for(int i = 1; i <= N; i++) {
            printf("%d %.2lf\n",node[i].index,node[i].money/100);
        }
    }
    return 0;
}

L2-010 排座位

/******************************************************************************************
Date: 2018/3/10 10:58
Author: Wen Yaxin

解題思路:用並查集來確定朋友關係,並用容器來存放每個人的敵人。
1.如果兩個人是朋友,且他們不在一個集合,對集合進行合併。因爲朋友的朋友也是朋友。
2.如果x,y兩個人是敵人,則把y放入x的容器,把x放入y的容器。
3.對於詢問,如果查明x,y在同一集合,則說明二者是朋友。此時在容器中查找是否敵對。
(1) x,y在同一集合,且x對應的容器中存有y。屬於有共同朋友,又彼此敵對。輸出"OK but...";
(2) x,y在同一集合,且x對應的容器中沒有y。屬於朋友關係,且不敵對,輸出"No problem";
(3) x,y在不同集合,且x對應的容器中存有y。二者不是朋友,且敵對。輸出"No way";
(4) x,y在不同集合,且x對應的容器中沒有y。二者不是朋友,且不敵對。輸出"Ok".

變量含義:
v:v[i]即容器i存放編號爲i的人的所有敵人
f:f[i]表示編號i的人所在的集合編號。

方法含義:
initSet:初始化集合和容器
findSet:尋找元素所屬集合
unionSet:集合合併
*********************************************************************************************/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>

using namespace std;

const int maxn = 105;
vector<int>v[maxn]; //存放某個人的敵人
int f[maxn];        //進行並查集操作。
void initSet(int N) {
    for(int i = 1; i <= N; i++) {
        f[i] = i;
        v[i].clear();
    }
}
int findSet(int x) {
    int temp = x;
    while(f[x] != x) {
        x = f[x];
    }
    //路徑壓縮。
    while(f[temp] != temp) {
        temp = f[temp];
        f[temp] = x;
    }
    return x;
}
void unionSet(int x,int y) {
    if(x != y) {
         f[x] = y;
    }
}
int main() {
    int N,M,K;
    int x,y,relation,fx,fy;
    while(~scanf("%d%d%d",&N,&M,&K)) {
        initSet(N);
        while(M--) {
            scanf("%d%d%d",&x,&y,&relation);
            if(relation == 1) {
                fx = findSet(x);
                fy = findSet(y);
                unionSet(fx,fy);
            }
            else {
                v[x].push_back(y);
                v[y].push_back(x);
            }
        }
        while(K--) {
            scanf("%d%d",&x,&y);
            fx = findSet(x);
            fy = findSet(y);
            bool flag = true;  //兩個人不是敵人
            for(int i = 0; i < (int)v[x].size(); i++) {
                if(v[x][i] == y) {
                    flag = false;  //兩個人是敵人
                    break;
                }
            }
            //兩個人是朋友。
            if(fx == fy) {
                if(flag) {
                    printf("No problem\n");
                }
                else {
                    printf("OK but...\n");
                }
            }
            else {
                if(flag) {
                    printf("OK\n");
                }
                else {
                    printf("No way\n");
                }
            }
        }
    }
    return 0;
}

L2-011 玩轉二叉樹

/*************************************************************************************
Date: 2018/3/10 13:52
Author: Wen Yaxin

解題思路:利用先序序列和中序序列確定二叉樹。
先序:根左右
中序:左根右
從中可知,給出先序序列,第一個元素就是根節點的值,則利用這個值,在中序
序列中查找,假設查找到第num個元素爲根,則中序,num左側序列爲左子樹的中序,
num右側序列爲右子樹的中序。劃分出左右子樹的先序和中序後再次求解。
求其鏡面樹:對於一個非空根節點,採用兩個整數交換的思想,對其進行左右根
節點的交換。
層次序列:對二叉樹進行廣度優先遍歷即可。

變量含義:
preOrder:存放題目給出的先序序列
inOrder:存放題目給出的中序序列
levelOrder:存放最終層次遍歷的結果
cnt:數組下標

方法含義:
CreateTree:根據中序和先序構建二叉樹
getSymmetryTree:獲取鏡像樹
getLevelOrder:獲取層次遍歷結果
*******************************************************************************************/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <stdlib.h>

using namespace std;

int preOrder[50];   //存放先序序列
int inOrder[50];    //存放中序序列
int levelOrder[50],cnt;
typedef struct Node {
    int data;
    struct Node *lchild;
    struct Node *rchild;
}node;
node* CreateTree(int *pre,int *in,int n) {
    node *b;
    int *p,k;
    if(n <= 0)  return NULL;   //節點個數小於等於0,是空樹。
    b = (node*)malloc(sizeof(node));
    b->data = *pre;            //先序序列的第一個元素爲根
    //已知根的值,在中序中確定序列。
    for(p = in; p < in+n; p++) {
        if(*p == *pre) {
            break;
        }
    }
    k = p-in;
    b->lchild = CreateTree(pre+1,in,k);
    b->rchild = CreateTree(pre+k+1,p+1,n-k-1);
    return b;
}
node* getSymmetryTree(node *root) {
    node *p; //輔助指針
    if(root != NULL) {
        p = root->lchild;
        root->lchild = root->rchild;
        root->rchild = p;
        getSymmetryTree(root->lchild);
        getSymmetryTree(root->rchild);
    }
    return root;
}
void getLevelOrder(Node *root) {
    queue<node*>qu;
    qu.push(root);
    Node *p;
    while(!qu.empty()) {
        p = qu.front();
        qu.pop();
        if(p != NULL) {
            levelOrder[cnt++] = p->data;
            if(p->lchild != NULL) {
                qu.push(p->lchild);
            }
            if(p->rchild != NULL) {
                qu.push(p->rchild);
            }
        }
    }
}
int main() {
    int n;
    while(~scanf("%d",&n)) {
        for(int i = 0; i < n; i++) {
            scanf("%d",&inOrder[i]);
        }
        for(int i = 0; i < n; i++) {
            scanf("%d",&preOrder[i]);
        }
        Node *root;
        root = CreateTree(preOrder,inOrder,n);
        getSymmetryTree(root);
        cnt = 0;
        getLevelOrder(root);
        for(int i = 0; i < n; i++) {
            printf("%d",levelOrder[i]);
            if(i != n-1) {
                printf(" ");
            }
        }
        printf("\n");
    }
    return 0;
}

L2-012 關於堆的判斷

/**********************************************************************************
Date: 2018/3/12 9:41
Author: Wen Yaxin

解題思路:
1.邊插入邊調整小頂堆。對於新插入的元素,如果比其根小,則
需要與其根調換位置,而這樣也會影響堆本身,因此可能需要多次
執行該操作。使小值不斷上浮。
2.由於題目中給出的元素的值可能有重複的。所以不能採用直接去
查找元素的下標。而應該枚舉根節點,根據根節點求左右節點。來
確定他們的關係。

變量含義:
n:n個元素
m:m次詢問
elem:存放n個元素的數組

方法含義:
Insert:插入第i個元素,並調整堆
createMinHeap:構造小頂堆
judge1:判斷一個元素是否爲根。
judge2:堆其餘三種情況進行判斷。
************************************************************************/
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 1010;
int n,m,elem[maxn];
void Insert(int i) {
    int temp = elem[i];
    while(i/2>0 && temp<elem[i/2]) {
        elem[i] = elem[i/2];
        i = i/2;
    }
    elem[i] = temp;
}
void createMinHeap() {
    for(int i = 2; i <= n; i++) {
        Insert(i);
    }
}
bool judge1(int num) {
    if(num == elem[1]) return true;
    else return false;
}
bool judge2(int num1,int num2,int type) {
    int lchild,rchild,i;
    //判斷兩個節點是否爲兄弟節點。
    if(type == 1) {
        for(i = 1; i <= n/2; i++) {
            lchild = 2*i;
            rchild = 2*i + 1;
            if(rchild <= n) {
                if(elem[lchild]==num1 && elem[rchild]==num2) return true;
                if(elem[lchild]==num2 && elem[rchild]==num1) return true;
            }
        }
        return false;
    }
    //判斷num1是否爲num2的父母
    if(type == 2) {
        for(i = 1; i <= n/2; i++) {
            lchild = 2*i;
            rchild = 2*i + 1;
            if(elem[i] == num1) {
                if(elem[lchild]==num2) return true;
                if(rchild<=n && elem[rchild]==num2) return true;
            }
        }
        return false;
    }
    //判斷num1是否是num2的孩子。
    for(i = 1; i <= n/2; i++) {
        lchild = 2*i;
        rchild = 2*i+1;
        if(elem[i] == num2) {
            if(elem[lchild]==num1) return true;
            if(rchild<=n && elem[rchild]==num1) return true;
        }
    }
    return false;
}
int main() {
    int num1,num2;
    char str[20];
    while(~scanf("%d%d",&n,&m)) {
        for(int i = 1; i <= n; i++) {
            scanf("%d",&elem[i]);
        }
        createMinHeap();
        getchar();
        bool flag;
        while(m--) {
            scanf("%d %s",&num1,str);
            //判斷是否爲兄弟的情況
            if(str[0]=='a') {
                scanf("%d%s%s",&num2,str,str);
                flag = judge2(num1,num2,1);
            }
            else {
                //判斷num1是否爲num2的孩子之一。
                scanf("%s",str);
                if(str[0] == 'a') {
                    scanf("%s%s%d",str,str,&num2);
                    flag = judge2(num1,num2,3);
                }
                else {
                    scanf("%s",str);
                    //判斷num1是否爲根
                    if(str[0] == 'r') {
                        flag = judge1(num1);
                    }
                    //判斷num1是否爲num2的父母
                    else {
                        scanf("%s%d",str,&num2);
                        flag = judge2(num1,num2,2);
                    }
                }
            }
            if(flag) {
                printf("T\n");
            }
            else {
                printf("F\n");
            }
        }
    }
    return 0;
}

L2-013 紅色警報

/**********************************************************************************
Date: 2018/3/12 17:32
Author: Wen Yaxin

解題思路:使用並查集,統計連通塊的個數。佔領一個點後,
要去除所有與其相連的邊,則佔領該點後,該點必孤立。該
點自己爲一個集合,如果該點原來就孤立,則佔領該點後連
通塊數不變,否則如果連通塊數只多了1,代表它自己貢獻了
那個1,其他情況,說明連通狀況改變了。


變量含義:
N:城市個數,城市編號0~N-1
M:M條邊,沒有自環,但是可能有重邊。
K:佔領K個點
edge:用來存邊
head:相當於鏈式存儲圖的鏈表頭節點
vis:vis用來標記u-v這條無向邊,該標記用來去除重邊
edgeNum:邊的數量,不帶重邊。
f:集合
isDel:標記某條邊是否被刪除。

方法含義:
init:初始化一些變量
initSet:初始化各個集合
findSet:尋找元素所在的集合
unionSet:合併集合
addEdge:加邊函數
getSetNum:統計集合個數,即連通塊的個數
delEdge:刪除與x相連的邊
**************************************************************************************/
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 505;
const int maxm = 5005;
int N,M,K;    
struct Edge{
    int u,v,nex;
}edge[maxm*2];
int head[maxn],vis[maxn][maxn],edgeNum,f[maxn];
int isDel[maxm*2]; 
void init() {
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(isDel,0,sizeof(isDel));
    edgeNum = 0;
}
void initSet() {
    for(int i = 0; i < N; i++) {
        f[i] = i;
    }
}
int findSet(int x) {
    while(x != f[x]) {
        x = f[x];
    }
    return x;
}
void unionSet(int x,int y) {
    if(x != y) {
        f[x] = y;
    }
}
void addEdge(int x,int y) {
    edge[edgeNum].u = x;
    edge[edgeNum].v = y;
    edge[edgeNum].nex = head[x];
    head[x] = edgeNum++;

    edge[edgeNum].u = y;
    edge[edgeNum].v = x;
    edge[edgeNum].nex = head[y];
    head[y] = edgeNum++;
}
int getSetNum() {
    int num = 0;
    for(int i = 0; i < N; i++) {
        if(f[i]==i) {
            num++;
        }
    }
    return num;
}
void delEdge(int x) {
    for(int i = head[x]; i != -1; i=edge[i].nex) {
        if(isDel[i]==0) {
            isDel[i] = isDel[i^1] = 1; 
            //一條邊和其反向邊是相鄰的。
        }
    }
}
int main() {
    int u,v,setNum,x,fu,fv;
    while(~scanf("%d%d",&N,&M)) {
        init();
        initSet();
        while(M--) {
            scanf("%d%d",&u,&v);
            if(!vis[u][v]) {
                addEdge(u,v);
                vis[u][v] = vis[v][u] = 1;
                fu = findSet(u);
                fv = findSet(v);
                unionSet(fu,fv);
            }
        }
        setNum = getSetNum();
        scanf("%d",&K);
        for(int i = 1; i <= K; i++) {
            scanf("%d",&x);
            delEdge(x);
            initSet();
            for(int j = 0; j < edgeNum; j++) {
                if(isDel[j]==0) {
                    fu = findSet(edge[j].u);
                    fv = findSet(edge[j].v);
                    unionSet(fu,fv);
                }
            }
            if(getSetNum()==setNum || setNum+1==getSetNum()) {
                printf("City %d is lost.\n",x);
            }
            else {
                printf("Red Alert: City %d is lost!\n",x);
            }
            setNum = getSetNum();
        }
        if(K == N) {
            printf("Game Over.\n");
        }
    }
    return 0;
}

L2-014 列車調度

/*********************************************************************************
Date: 2018/3/12 19:50
Author: Wen Yaxin

解題思路:LIS 求解,LIS ->longest increasing sequence
8 4 2 5 3 9 1 6 7

四個軌道:
1 2 4 8
    3 5
    6 9
      7
8比9小,所以8先在道上等待。當前狀況如下:
軌道1 :8
--------------------------------------------
然後4,4比8小,則4比8後到,和8停同一個軌道等待,當前狀況如下。
軌道1 :4 8
-------------------------------------------
然後2,2比4小,2比4後到,和4停同一個軌道等待,當前狀況如下。
軌道1:2 4 8
----------------------------------------------
然後5,5比2大,則5比2先到,則需要令起軌道。當前狀況如下。
軌道1:2 4 8
軌道2:5
----------------------------------------------
然後3,3比2大,則3比2先到,3比5小,3比5後到,則3在5後等待,當前狀況如下。
軌道1:2 4 8
軌道2:3 5
-----------------------------------------------------------------
然後9,9比2大,9比3大,因此9需要令起軌道。當前狀況如下。
軌道1:2 4 8
軌道2:3 5
軌道3:9
---------------------------------------------------------
然後1,1比2小,則1比2後到,可以排在2後面,當前狀況如下。
軌道1:1 2 4 8
軌道2:3 5
軌道3:9
--------------------------------------------------
然後6,6比1和3都大,則6比1和3先到,6無法排在1,3後,但6比9小,當前狀況如下
軌道1:1 2 4 8
軌道2:3 5
軌道3:6 9
---------------------------------------------
然後7,7比1,3,6,都大,無法排在他們後,則令起軌道,當前狀況如下。
軌道1:1 2 4 8
軌道2:3 5
軌道3:6 9
軌道4:7
-----------------------------------------------------------
從推導過程可以看出,對於當前要進入軌道的列車,如果以存在的軌道上排列的
最小編號的火車的值比當前值大,則他們位於同一軌道,否則當前列車必須令
起軌道。這個問題就是LIS,LIS有一個logN的算法,其思想和推導過程如出一轍,
如果當前值比LIS的最後一個值大,則這個元素就是LIS新的末尾,否則二分查找
LIS中第一個大於該元素的數,並替換這個數。最後LIS的長度就是答案。

變量含義:
LIS:輔助求取最長遞增子序列的長度,但數組內部可能並非是最長遞增子序列對應的值。
x:用來接收題目給出的一個個元素

方法含義:
binarySearch:用來查找LIS中第一個大於value的值的位置。
******************************************************************************************/
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 1e5+10;
int lis[maxn],x;  //求最長上升子序列。
int binarySearch(int len,int value) {
    int low,high,mid;
    low = 1;
    high = len;
    while(low < high) {
        mid = (low+high)/2;
        if(lis[mid]>=value) {
            high = mid;
        }
        else {
            low = mid+1;
        }
    }
    return low;
}
int main() {
    int n,x;
    while(~scanf("%d",&n)) {
        int len = 1;
        scanf("%d",&lis[1]);
        for(int i = 1; i < n; i++) {
            scanf("%d",&x);
            if(x > lis[len]) {
                len++;
                lis[len] = x;
            }
            else {
                //查找數組中第一個比x大的整數。
                int pos = binarySearch(len,x);
                lis[pos] = x;
            }
        }
        printf("%d\n",len);
    }
    return 0;
}

L2-015 互評成績

/****************************************************************
Date: 2018/3/12 20:29
Author: Wen Yaxin

解題思路:水題,按照題目描述求解即可。

變量含義:
score:存放n個人的成績。
************************************************************/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 1e4+10;
double score[maxn];
int main() {
    int n,k,m;
    double Min,Max,sum,s;
    while(~scanf("%d%d%d",&n,&k,&m)) {
        for(int i = 0; i < n; i++) {
            scanf("%lf",&Min);
            sum = Max = Min;
            for(int j = 1; j < k; j++) {
                scanf("%lf",&s);
                sum += s;
                Max = max(Max,s);
                Min = min(Min,s);
            }
            score[i] = (sum-Max-Min)/(k-2);
        }
        sort(score,score+n);
        for(int i = n-m; i < n; i++) {
            printf("%.3lf",score[i]);
            if(i != n-1) {
                printf(" ");
            }
        }
        printf("\n");
    }
    return 0;
}

L2-016 願天下有情人都是失散多年的兄妹

/*********************************************************************************
Date 2018/3/16 18:03
Author Wen Yaxin

解題思路:
首先一個人,五代之內的家屬最多 1+2+4+8+16 = 31.
因此可以先將人物關係存儲起來,如果a是b的父母,則建一條由b指向
a的邊。採用暴力的思想,對於一個編號存在的人,把它無代以內的家
屬包括它自己存在它對應的容器中。

注意:不要只顧存直接給出性別的人的性別,也要存父母的性別,這是
題目中暗示的,忘記存儲的話只能過兩組數據。

變量含義:
cnt:作爲edge的下標,每存一條邊,右移一位。
sex:sex[i]存放性別,sex[i]爲0,代表編號爲i的人不存在,sex[i]=1代表男,sex[i]=2代表女
head:頭節點。
V:v[i]是一個vector,用來存放編號爲i的人五代以內的所有家屬
edge:存放給出的關係
node:節點,成員變量有,代數和人員編號

方法含義:
addEdge:加邊函數
bfs:bfs(i),求編號爲i的人五代以內的所有親屬,並存入其編號對應的vector
*****************************************************************************************/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <queue>

using namespace std;

const int maxn = 1e5+10;
int cnt,sex[maxn],head[maxn];
vector<int>v[maxn];
struct Edge{
    int v;
    int nex;
}edge[maxn*10];
void addEdge(int u,int v) {
    edge[cnt].v = v;
    edge[cnt].nex = head[u];
    head[u] = cnt++;
}
typedef struct Node {
    int u;
    int level;
}node;
void bfs(int x) {
    queue<node>qu;
    node cur,nex;
    cur.u = x;
    cur.level = 1;
    qu.push(cur);
    int temp;
    while(!qu.empty()) {
        cur = qu.front();
        qu.pop();
        if(cur.level > 5) {
            continue;
        }
        temp = cur.u;
        v[x].push_back(temp);
        for(int i = head[temp]; i != -1; i = edge[i].nex) {
            nex.u = edge[i].v;
            nex.level = cur.level + 1;
            qu.push(nex);
        }
    }
}
int main() {
    int n,me,fa,mo,m;
    char ch;
    while(~scanf("%d",&n)) {
        for(int i = 0; i < maxn; i++) {
            v[i].clear();
        }
        cnt = 0;
        memset(head,-1,sizeof(head));
        memset(sex,0,sizeof(sex));
        for(int i = 0; i < n; i++) {
            scanf("%d %c%d%d",&me,&ch,&fa,&mo);
            if(ch == 'M') {
                sex[me] = 1;  //是男性。
            }
            else {
                sex[me] = 2;
            }
            if(fa != -1) {
                addEdge(me,fa);
                sex[fa] = 1; //不要忘存父母的性別
            }
            if(mo != -1) {
                addEdge(me,mo);
                sex[mo] = 2; //不要忘存父母的性別
            }
        }
        for(int i = 0; i < maxn; i++) {
            //該編號存在
            if(sex[i] != 0) {
                bfs(i);
            }
        }
        scanf("%d",&m);
        int x,y;
        while(m--) {
            scanf("%d%d",&x,&y);
            //同性不可通婚。
            if(sex[x] == sex[y]) {
                printf("Never Mind\n");
            }
            else {
                //判斷無代內是否有共同親人。
                bool ok = true;
                for(int i = 0; i < (int)v[x].size(); i++) {
                    for(int j = 0; j < (int)v[y].size(); j++) {
                        if(v[x][i] == v[y][j]) {
                            ok = false;
                            break;
                        }
                    }
                    if(!ok) break;
                }
                if(ok) {
                    printf("Yes\n");
                }
                else {
                    printf("No\n");
                }
            }
        }
    }
    return 0;
}

L2-017 人以羣分

/*******************************************************
Date 2018/3/13 19:21
Author Wen Yaxin

解題思路:對數組排序,然後儘可能平分。

變量含義:
active:存放每個人的活躍度
*******************************************************/
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstdio>

using namespace std;

const int maxn = 1e5+10;
int active[maxn];
int main() {
    int n,tmp1,tmp2;
    while(~scanf("%d",&n)) {
        for(int i = 0; i < n; i++) {
            scanf("%d",&active[i]);
        }
        sort(active,active+n);
        tmp1 = n/2;
        tmp2 = n-tmp1;
        printf("Outgoing #: %d\n",max(tmp1,tmp2));
        printf("Introverted #: %d\n",min(tmp1,tmp2));
        int limit = tmp1;
        tmp1 = tmp2 = 0;
        for(int i = 0; i < limit; i++) {
            tmp1 += active[i];
        }
        for(int i = limit; i < n; i++) {
            tmp2 += active[i];
        }
        printf("Diff = %d\n",abs(tmp2-tmp1));
    }
    return 0;
}

L2-018 多項式A除以B




L2-019 悄悄關注

/***********************************************************************************
Date 2018/3/13 20:30
Author Wen Yaxin

解題思路:對於判斷姓名存不在可以通過map映射,或者對關注的人
排序,然後再對m個人進行二分查找。

變量含義:
arr1:存放關注列表中的人。
arr2:存放輸入的m個名字
num:存放輸入的m個人對應的點贊數量
ans:存放答案

方法含義:
binarySearch:二分查找某個名字是否存在。
**********************************************************************************/
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstdio>

using namespace std;

const int maxn = 10010;
string arr1[maxn],arr2[maxn],ans[maxn];
double num[maxn];
int binarySearch(string s,int len) {
    int low,high,mid;
    low = 0;
    high = len;
    while(low <= high) {
        mid = (low+high)/2;
        if(arr1[mid] == s) {
            return mid;
        }
        else if(s < arr1[mid]) {
            high = mid-1;
        }
        else {
            low = mid+1;
        }
    }
    return -1;
}
int main() {
    int n,m,cnt;
    while(~scanf("%d",&n)) {
        for(int i = 0; i < n; i++) {
            cin>>arr1[i];
        }
        sort(arr1,arr1+n);   //按照名字從小到大排序
        double aver,sum = 0;
        cin>>m;
        for(int i = 0; i < m; i++) {
            cin>>arr2[i]>>num[i];
            sum += num[i];
        }
        aver = sum/m;
        cnt = 0;
        for(int i = 0; i < m; i++) {
            if(num[i]>aver) {
                if(binarySearch(arr2[i],n-1) == -1) {
                    ans[cnt++] = arr2[i];
                }
            }
        }
        if(cnt == 0) {
            cout<<"Bing Mei You"<<endl;
        }
        else {
            sort(ans,ans+cnt);
            for(int i = 0; i < cnt; i++) {
                cout<<ans[i]<<endl;
            }
        }
    }
    return 0;
}

L2-020 功夫傳人

/********************************************************************
Date 2018/3/15 20:26
Author Wen Yaxin

解題思路:
去年天梯賽打了170分,做了這個題目,當時感覺給出的輩分順序
不會嚴格按照輩分來,但是自己就是按這種想法寫的,而且提交過了,當時以爲是僥倖,
今年又寫了一次,才發現題目中說了嚴格按照輩分給出,才發現本身
就是嚴格按輩分給的數據,所以題目就會便得很簡單。
按照題目描述做就好,只要輸入的關係普嚴格按照輩分,這個題目就是水題。

變量含義:
p:存放每個人的信息,其師傅編號,是否得道者,其功力值。
****************************************************************/
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<cmath>
#define INF 999999

using namespace std;

struct people{
    int parent;     //父母
    double power;   //功力
    int flag;       //是否是得道者
}p[100005];
int main(){
    //N整個師門總人數,Z祖師爺的功力值,R功夫折扣百分比,
    int N,K,fa,t;
    double Z,R;
    while(~scanf("%d%lf%lf",&N,&Z,&R)){
            p[0].power = Z;   //0用來存放祖師爺
            for(int i = 0; i < N; i++){
                scanf("%d",&K);
                //K = 0代表該人是得道者
                if(K == 0)   
                {
                    //flag爲1代表是得道者
                    p[i].flag = 1;   
                    //輸入功力被放大的倍數
                    scanf("%d",&t);   
                    //祖師爺是得道者
                    if(i == 0){
                        p[0].power = p[0].power*t;
                    }
                    else{   //其他人是得到者,對其師傅功力打折扣後再放大相應倍數。
                        fa = p[i].parent;
                        p[i].power = p[fa].power*(100-R)/100*t;
                    }
                }
                else
                {
                    for(int j = 0; j < K; j++)
                    {
                        scanf("%d",&t);
                        p[t].parent = i;
                        p[t].flag = 0;   //不是得道者。
                        p[t].power = p[i].power*(100-R)/100;
                    }
                }
            }
            double ans = 0;
            for(int i = 0; i < N; i++)
            {
                if(p[i].flag == 1)
                {
                    ans = ans + p[i].power;
                }
            }
            printf("%d\n",(int)ans);
    }
    return 0;
}

L2-021 點贊狂魔


/******************************************************************************
Date 2018/3/16 8:44
Author Wen Yaxin

解題思路:
使用數組來標記每個數字是否出現過,來統計不同數字的個數。
最後算出每個數字出現的平均次數,進行結構體排序。

變量含義:
vis:vis用來標記,如果vis[i] = k,表示第k個人點贊過i這種類型。
node:存放每個人的點贊信息

方法含義:
cmp:定義結構體排序的規則。
***********************************************************************************/
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>

using namespace std;

const int maxn = 1e7+10;
int vis[maxn];
struct Node {
    char name[30];
    int num;
    double aver;
}node[200];
bool cmp(Node a,Node b) {
    if(a.num == b.num) {
        return a.aver > b.aver;
    }
    else return a.num < b.num;
}
int main() {
    int n,k,x;
    while(~scanf("%d",&n)) {
        for(int i = 0; i < maxn; i++) {
            vis[i] = -1;
        }
        for(int i = 0; i < n; i++) {
            scanf("%s",node[i].name);
            node[i].num = 0;
            scanf("%d",&k);
            for(int j = 0; j < k; j++) {
                scanf("%d",&x);
                if(vis[x] != i) {
                    vis[x] = i;
                    node[i].num++;
                }
            }
            if(node[i].num == 0) {
                node[i].aver = 0;
            }
            else {
                node[i].aver = (k*1.0)/node[i].num;
            }
        }
        sort(node,node+n,cmp);
        if(n < 3) {
            for(int i = n-1; i >=0 ; i--) {
                printf("%s ",node[i].name);
            }
            for(int i = 0; i < 3-n; i++) {
                printf("-");
                if(i != 2-n) {
                    printf(" ");
                }
            }
        }
        else {
            for(int i = n-1; i >= n-3; i--) {
                printf("%s",node[i].name);
                if(i != n-3) {
                    printf(" ");
                }
            }
        }
        printf("\n");
    }
    return 0;
}

L2-022 鏈表重排

/***********************************************************************
Date 2018/3/16 14:41
Author Wen Yaxin

解題思路:
按照鏈表的順序,順序把節點存儲下來,然後分別定義兩個指針
指向首尾,讓兩個指針不斷向中間靠攏。

坑點:輸入的節點不一定都在鏈表裏面。這點超級坑,錯了好多次。
例如:
1 4
1 1 2
1 2 3
3 3 -1
4 4 -1

1->2->3 是一條鏈表,但是4 4 -1,只是一個無用的節點。
如果不考慮,則會過不去測試點3,裏面的數據一個都過不去。


變量含義:
node:存放題目輸入的數據
arr:順序存儲鏈表節點的信息
ans:存儲答案。
***************************************************************************/
#include <iostream>
#include <stdio.h>

using namespace std;
const int maxn = 1e6+10;
struct Node {
    int data;
    int nex;
}node[maxn];
struct Elem {
    int add;
    int data;
    int nex;
}arr[maxn],ans[maxn];
int main() {
    int head,n;
    int x,y,z,i,j,k;
    while(~scanf("%d%d",&head,&n)) {
        for(i = 0; i < n; i++) {
            scanf("%d%d%d",&x,&y,&z);
            node[x].data = y;
            node[x].nex = z;
        }
        int cnt = 0;
        while(head != -1) {
            arr[cnt].add = head;
            arr[cnt++].data = node[head].data;
            head = node[head].nex;
        }
        i = 0;
        j = cnt-1;
        //這裏是cnt-1,而不是n-1,可能有元素不再鏈表中。
        k = 0;
        while(i <= j) {
            if(i == j) {
                ans[k].add = arr[i].add;
                ans[k].data = arr[i].data;
                ans[k++].nex = -1;
                break;
            }
            ans[k].add = arr[j].add;
            ans[k].data = arr[j].data;
            ans[k++].nex = arr[i].add;
            j--;
            ans[k].add = arr[i].add;
            ans[k].data = arr[i].data;
            ans[k++].nex = arr[j].add;
            i++;
        }
        //printf("%d %d\n",i,j);
        ans[cnt-1].nex = -1;
        for(i = 0; i < cnt-1; i++) {
            printf("%05d %d %05d\n",ans[i].add,ans[i].data,ans[i].nex);
        }
        printf("%05d %d -1\n",ans[cnt-1].add,ans[cnt-1].data);
    }
    return 0;
}

L2-023 着色圖問題



L2-024 部落

/******************************************************************************
Date 2018/3/16 17:07
Author Wen Yaxin

解題思路:
用數組標記人的編號,然後用並查集對集合進行合併,但是需要確定
集合合併的方向。即前一個人要合併到後一個人上,或反過來,但是
一定要有一個合併的準則。

注意:查詢元素所屬集合過程要進行路徑壓縮,否則有一組數據將會
運行超時。所謂路徑壓縮很簡單,即找到元素所屬集合後,把路徑中
的所有元素的所屬集合直接指向所屬集合的編號,而不是指向其前驅。

變量含義:
f:f[i]存放i所屬的集合。
vis:vis[i]標記編號爲i的人是否存在。

方法含義:
initSet:集合初始化
findSet:查找元素所在集合
unionSet:集合合併
******************************************************************************/
#include <iostream>
#include <stdio.h>
#include <string.h>

using namespace std;

const int maxn = 1e4+3;
int f[maxn],vis[maxn];
void initSet() {
   for(int i = 0; i < maxn; i++) {
        f[i] = i;
   }
}
int findSet(int x) {
   int temp = x;
   while(x != f[x]) {
         x = f[x];
   }
   //路徑壓縮。不壓縮的話,有一組數據超時
   while(temp != f[temp]) {
        temp = f[temp];
        f[temp] = x;
   }
   return x;
}
void unionSet(int x,int y) {
   f[x] = y;
}
int main() {
    int n,k,c,cnt,num,fu,fv;
    while(~scanf("%d",&n)) {
        memset(vis,0,sizeof(vis));
        cnt = num = 0;
        initSet();
        while(n--) {
            scanf("%d",&k);
            if(k) {
                scanf("%d",&c);
                if(!vis[c]) {
                    vis[c] = 1;
                    cnt++;
                }
                fu = findSet(c);
                for(int j = 1; j < k; j++) {
                    scanf("%d",&c);
                    if(!vis[c]) {
                        vis[c] = 1;
                        cnt++;
                    }
                    fv = findSet(c);
                    if(fu != fv) {
                        unionSet(fu,fv);
                    }
                    fu = findSet(c);
                }
            }
        }
        for(int i = 1; i < maxn; i++) {
            if(vis[i]==1 && findSet(i)==i) {
                num++;
            }
        }
        printf("%d %d\n",cnt,num);
        scanf("%d",&c);
        int u,v;
        while(c--) {
            scanf("%d%d",&u,&v);
            fu = findSet(u);
            fv = findSet(v);
            if(fu == fv) {
                puts("Y");
            }
            else {
                puts("N");
            }
        }
    }
    return 0;
}






發佈了386 篇原創文章 · 獲贊 150 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章