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