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