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;
}






  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值