惹某突然咕咕的3+4周(习题+感悟)

(一)并查集

A - How Many Tables HDU - 1213

题目描述

    Today is Ignatius’ birthday. He invites a lot of friends. Now it’s dinner time. Ignatius wants to know how many tables he needs at least. You have to notice that not all the friends know each other, and all the friends do not want to stay with strangers.
One important rule for this problem is that if I tell you A knows B, and B knows C, that means A, B, C know each other, so they can stay in one table.
For example: If I tell you A knows B, B knows C, and D knows E, so A, B, C can stay in one table, and D, E have to stay in the other one. So Ignatius needs 2 tables at least.

Input

The input starts with an integer T(1<=T<=25) which indicate the number of test cases. Then T test cases follow. Each test case starts with two integers N and M(1<=N,M<=1000). N indicates the number of friends, the friends are marked from 1 to N. Then M lines follow. Each line consists of two integers A and B(A!=B), that means friend A and friend B know each other. There will be a blank line between two cases.

Output

For each test case, just output how many tables Ignatius needs at least. Do NOT print any blanks.

Sample Input

2
5 3
1 2
2 3
4 5

5 1
2 5

Sample Output

2
4

理解

这是一道欢乐敲模板的题目QAq
题意大致是互相认识的人可以坐在同一张桌子上,例如a和b认识,b和c认识,那么abc可以坐在一章桌子上,问最少需要多少桌子

AC代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1050;
int s[maxn];
void init_set(){
	for(int i = 1;i <= maxn;i++){
		s[i] = i;
	}
}
int find_set(int k){
	return k == s[k]?k:find_set(s[k]);
}
void union_set(int x,int y){
	x = find_set(x);
	y = find_set(y);
	if(x != y) s[x] = s[y];
}
int main(){
	int t; cin >> t;
	while(t--){
		int n,m; cin>> n >> m;
		init_set();
		for(int i = 1;i <= m;i++){
			int x,y; cin >> x >> y;
			union_set(x,y);
		}
		int cnt = 0;
		for(int i = 1;i <= n;i++){
			if(s[i] == i) cnt++;
		}
		cout << cnt << endl;
	}
	return 0;
}

B - Ubiquitous Religions POJ - 2524

题目描述

    There are so many different religions in the world today that it is difficult to keep track of them all. You are interested in finding out how many different religions students in your university believe in.
You know that there are n students in your university (0 < n <= 50000). It is infeasible for you to ask every student their religious beliefs. Furthermore, many students are not comfortable expressing their beliefs. One way to avoid these problems is to ask m (0 <= m <= n(n-1)/2) pairs of students and ask them whether they believe in the same religion (e.g. they may know if they both attend the same church). From this data, you may not know what each person believes in, but you can get an idea of the upper bound of how many different religions can be possibly represented on campus. You may assume that each student subscribes to at most one religion.

Input

    The input consists of a number of cases. Each case starts with a line specifying the integers n and m. The next m lines each consists of two integers i and j, specifying that students i and j believe in the same religion. The students are numbered 1 to n. The end of input is specified by a line in which n = m = 0.

Output

    For each test case, print on a single line the case number (starting with 1) followed by the maximum number of different religions that the students in the university believe in.

Sample Input

10 9
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
10 4
2 3
4 5
4 8
5 8
0 0

Sample Output

Case 1: 1
Case 2: 7

理解

一共n个学生,学生编号从1到n。和m行信息,每行包含两个整数 i 和 j ,表示学生i和j信仰相同的宗教。
求一个学校的学生信仰宗教的最大种类数(同上一题!敲敲模板)

AC代码

#include <bits/stdc++.h>
using namespace std;
int mp[105][105];
int main(){
    int n,m; cin >> n >> m;
    memset(mp,0,sizeof(mp));
    for(int i = 1;i <= m;i++){
        int x,y; cin >> x >> y;
        mp[x][y] = 1;
    }
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= n;j++){
            for(int k = 1;k <= n;k++){
                if(mp[j][i] && mp[i][k]) mp[j][k] = 1;
            }
        }
    }
    int sum;
    int cnt = 0;
    for(int i = 1;i <= n;i++){
        sum = 0;
        for(int j = 1;j <= n;j++){
            if(mp[i][j]) sum++;
        }
        if(sum >= (n+1)/2) cnt++;
        sum = 0;
        for(int j = 1;j <= n;j++){
            if(mp[j][i]) sum++;
        }
        if(sum >= (n+1)/2) cnt++;
    }
    cout << cnt << endl;
    return 0;
}

C - The Suspects POJ - 1611

题目描述

    Severe acute respiratory syndrome (SARS), an atypical pneumonia of unknown aetiology, was recognized as a global threat in mid-March 2003. To minimize transmission to others, the best strategy is to separate the suspects from others.
In the Not-Spreading-Your-Sickness University (NSYSU), there are many student groups. Students in the same group intercommunicate with each other frequently, and a student may join several groups. To prevent the possible transmissions of SARS, the NSYSU collects the member lists of all student groups, and makes the following rule in their standard operation procedure (SOP).
Once a member in a group is a suspect, all members in the group are suspects.
However, they find that it is not easy to identify all the suspects when a student is recognized as a suspect. Your job is to write a program which finds all the suspects.

Input

    The input file contains several cases. Each test case begins with two integers n and m in a line, where n is the number of students, and m is the number of groups. You may assume that 0 < n <= 30000 and 0 <= m <= 500. Every student is numbered by a unique integer between 0 and n−1, and initially student 0 is recognized as a suspect in all the cases. This line is followed by m member lists of the groups, one line per group. Each line begins with an integer k by itself representing the number of members in the group. Following the number of members, there are k integers representing the students in this group. All the integers in a line are separated by at least one space.
A case with n = 0 and m = 0 indicates the end of the input, and need not be processed.

Output

    For each case, output the number of suspects in one line.

Sample Input

100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2
200 2
1 5
5 1 2 3 4 5
1 0
0 0

Sample Output

4
1
1

理解

一旦一组中有一个可能的患者, 组内的所有成员就都是可能的患者。
一开始只有0号学生被视为可能的患者。

AC代码

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 30005;
int s[maxn];

int find_set(int k){
	if(k == s[k]) return k;
	return s[k] = find_set(s[k]);
}
void union_set(int x,int y){
	x = find_set(x);
	y = find_set(y);
	if(x != y) s[x] = s[y];
}
int main(){
	int n,m;
	int t = 0;
	while(~scanf("%d %d",&n,&m)){
		if(n == 0 && m == 0) break;
		for(int i = 0;i < n;i++) s[i] = i;
		int cnt = 0;
		for(int i = 1;i <= m;i++){
			int k; scanf("%d",&k);
			int a;
			if(k){
				scanf("%d",&a);
				k--;
			}
			while(k--){
				int x; scanf("%d",&x);
				union_set(a,x);
			}	
		}
		for(int i = 0;i < n;i++){
			if(find_set(i) == find_set(0)) cnt++;
		}
		printf("%d\n",cnt);
	}
	return 0;
}

D - Find them, Catch them POJ - 1703

题目描述

    The police office in Tadu City decides to say ends to the chaos, as launch actions to root up the TWO gangs in the city, Gang Dragon and Gang Snake. However, the police first needs to identify which gang a criminal belongs to. The present question is, given two criminals; do they belong to a same clan? You must give your judgment based on incomplete information. (Since the gangsters are always acting secretly.)
Assume N (N <= 10^5) criminals are currently in Tadu City, numbered from 1 to N. And of course, at least one of them belongs to Gang Dragon, and the same for Gang Snake. You will be given M (M <= 10^5) messages in sequence, which are in the following two kinds:

  1. D [a] [b]
    where [a] and [b] are the numbers of two criminals, and they belong to different gangs.
  2. A [a] [b]
    where [a] and [b] are the numbers of two criminals. This requires you to decide whether a and b belong to a same gang.

Input

The first line of the input contains a single integer T (1 <= T <= 20), the number of test cases. Then T cases follow. Each test case begins with a line with two integers N and M, followed by M lines each containing one message as described above.

Output

For each message “A [a] [b]” in each case, your program should give the judgment based on the information got before. The answers might be one of “In the same gang.”, “In different gangs.” and “Not sure yet.”

Sample Input

1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4

Sample Output

Not sure yet.
In different gangs.
In the same gang.

理解

这题有一点特殊就在于他一共只有两个帮派,然后就会出现敌人的敌人就是朋友这样的局面,然后敲模板!水过

AC代码

#include<iostream>
#include<cstdio>
#include<cstring> 
using namespace std;
const int maxn = 1e5 + 5;
int em[maxn];
int s[maxn];

int find_set(int k){
	if(k == s[k]) return k;
	return s[k] = find_set(s[k]);
}
void union_set(int x,int y){
	x = find_set(x);
	y = find_set(y);
	if(x != y) s[x] = y;
}
int main(){
	int t; scanf("%d",&t);
	while(t--){
		memset(em,0,sizeof(em));
		int n,m; scanf("%d %d",&n,&m);
		getchar();
		for(int i = 1;i <= n;i++){
			s[i] = i;
		}
		for(int i = 0;i < m;i++){
			char c; int a,b;
			scanf("%c %d %d",&c,&a,&b);
			getchar();
			if(c == 'D'){
				if(em[a]) union_set(em[a],b);
				if(em[b]) union_set(em[b],a);
				em[a] = b; em[b] = a;
			}
			else{
				if(find_set(a) == find_set(em[b])) printf("In different gangs.\n");
				else if(find_set(a) == find_set(b)) printf("In the same gang.\n");
				else printf("Not sure yet.\n");
			}
		}
	} 
	return 0;
} 

E - Cube Stacking POJ - 1988

题目描述

    
Farmer John and Betsy are playing a game with N (1 <= N <= 30,000)identical cubes labeled 1 through N. They start with N stacks, each containing a single cube. Farmer John asks Betsy to perform P (1<= P <= 100,000) operation. There are two types of operations:
moves and counts.

  • In a move operation, Farmer John asks Bessie to move the stack containing cube X on top of the stack containing cube Y.
  • In a count operation, Farmer John asks Bessie to count the number of cubes on the stack with cube X that are under the cube X and report that value.
    Write a program that can verify the results of the game.

Input

  • Line 1: A single integer, P
  • Lines 2…P+1: Each of these lines describes a legal operation. Line 2 describes the first operation, etc. Each line begins with a ‘M’ for a move operation or a ‘C’ for a count operation. For move operations, the line also contains two integers: X and Y.For count operations, the line also contains a single integer: X.
    Note that the value for N does not appear in the input file. No move operation will request a move a stack onto itself.

Output

Print the output from each of the count operations in the same order as the input file.

Sample Input

6
M 1 6
C 1
M 2 4
M 2 6
C 3
C 4

Sample Output

1
0
2

理解

n个方块,p组操作。操作有两种,M a b 是将含有a的堆放在含有b的堆上,C a 是输出 在含有a的堆里a下面共有多少个方块
带权并查集,让一个堆中最上面的方块为父节点,只需查找记录更新该点的父节点(最高点)然后记录到父节点的距离和方块的数量,是一个带权并查集问题

AC代码

#include<iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 3e4 + 5;
int s[maxn];
int v[maxn]; //到最高点的距离 
int rank[maxn]; 
void init(){
	for(int i = 0;i <= maxn;i++){
		s[i] = i;
		v[i] = 0;
		rank[i] = 1; 
	}
}
int find_set(int k){
	if(k != s[k]){ //如果他不是自己的最高点 
		int t = s[k]; //记录最高点 
		s[k] = find_set(s[k]); //更新最高点 
		v[k] += v[t]; //距离加上它的最高点的父节点的距离 
	}
	return s[k]; 
}
void union_set(int x,int y){
	x = find_set(x);
	y = find_set(y);
	if(x != y){
		s[y] = x;
		v[y] = rank[x];
		rank[x] += rank[y];
	}
}
int main(){
	int n;
	while(~scanf("%d",&n)){
		init();
		while(n--){
			getchar();
			char c;
			scanf("%c",&c);
			int x,y;
			if(c == 'M'){
				scanf("%d %d",&x,&y);
				union_set(x,y);
			}
			else{
				scanf("%d",&x);
				int a = find_set(x);
				printf("%d\n",rank[a]-v[x]-1);
			}
		}	
	}
	return 0;
}

(二)洛谷

A - P1443 马的遍历

题目描述

有一个n*m的棋盘(1<n,m<=400),在某个点上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步

Input

一行四个数据,棋盘的大小和马的坐标

Output

一个n*m的矩阵,代表马到达某个点最少要走几步(左对齐,宽5格,不能到达则输出-1)

Sample Input

3 3 1 1

Sample Output

0 3 2
3 -1 1
2 1 4

理解

这道题考的就是普通深搜昂,记录第一次到达该点时的步数一定是最优步数(废话)

AC代码

#include<bits/stdc++.h>
using namespace std;
int n,m,x,y;
int mp[500][500];
int st,en;
int dir[8][2]={{1,2},{1,-2},{-1,2},{-1,-2},{2,1},{2,-1},{-2,1},{-2,-1}};
struct node{
	int xx,yy;
}rec[300005];
void bfs(){
	mp[x][y] = 0; rec[1].xx = x; rec[1].yy = y;
	st = 1; en = 2;
	while(st < en){
		int sx = rec[st].xx;
		int sy = rec[st].yy;
		for(int i = 0;i < 8;i++){
			int x1 = sx + dir[i][0];
			int y1 = sy + dir[i][1];
			if(x1>=1&&x1<=n&&y1>=1&&y1<=m){
				if(mp[x1][y1] == -1){
					mp[x1][y1] = mp[sx][sy]+1;
					rec[en].xx = x1;
					rec[en].yy = y1;
					en++;
				}
			}
		}
		st++;
//		for(int i = 1;i <= n;i++){
//			for(int j = 1;j <= m;j++){
//				printf("%-5d",mp[i][j]);
//			}
//			cout << endl;
//		}
//		cout << endl;
	}
}

int main(){
	cin >> n >> m >> x >> y;
	memset(mp,-1,sizeof(mp));
	bfs();
	for(int i = 1;i <= n;i++){
		for(int j = 1;j <= m;j++){
			printf("%-5d",mp[i][j]);
		}
		cout << endl;
	}
	return 0;
}

(三)pta

6-5 链表逆置 (20 分)

题目描述

    本题要求实现一个函数,将给定单向链表逆置,即表头置为表尾,表尾置为表头。链表结点定义如下:

struct ListNode {
    int data;
    struct ListNode *next;
};
函数接口定义:
struct ListNode *reverse( struct ListNode *head );
其中head是用户传入的链表的头指针;函数reverse将链表head逆置,并返回结果链表的头指针。

裁判测试程序样例:

#include <stdio.h>
#include <stdlib.h>

struct ListNode {
    int data;
    struct ListNode *next;
};

struct ListNode *createlist(); /*裁判实现,细节不表*/
struct ListNode *reverse( struct ListNode *head );
void printlist( struct ListNode *head )
{
     struct ListNode *p = head;
     while (p) {
           printf("%d ", p->data);
           p = p->next;
     }
     printf("\n");
}

int main()
{
    struct ListNode  *head;

    head = createlist();
    head = reverse(head);
    printlist(head);
	
    return 0;
}
/* 你的代码将被嵌在这里 */

Sample Input

1 2 3 4 5 6 -1

Sample Output

6 5 4 3 2 1

理解

啊这题磕了好久指针老是指空(指针满天飞?)然后发现光定义的话指针会随机指向不是指空所以会错(段错误)所以改了一下子就A了,这题还问了紫妈和某龍大佬。。刚学链表果然就是菜是原罪。

AC代码

struct ListNode *reverse(struct ListNode *head){
	struct ListNode *temp;
    temp = NULL;
	while(head){
		struct ListNode *t = (struct ListNode *)malloc(sizeof(struct ListNode));
		t->next = NULL;
		t->data = head->data;
		t->next = temp;
		temp = t;
		head = head->next;
	}
	return temp;
}

6-6 两个有序链表序列的合并 (15 分)

题目描述

本题要求实现一个函数,将两个链表表示的递增整数序列合并为一个非递减的整数序列。
函数接口定义:
List Merge( List L1, List L2 );
其中List结构定义如下:
typedef struct Node PtrToNode;
struct Node {
ElementType Data; /
存储结点数据 /
PtrToNode Next; /
指向下一个结点的指针 /
};
typedef PtrToNode List; /
定义单链表类型 */
L1和L2是给定的带头结点的单链表,其结点存储的数据是递增有序的;函数Merge要将L1和L2合并为一个非递减的整数序列。应直接使用原序列中的结点,返回归并后的带头结点的链表头指针。
裁判测试程序样例:

#include <stdio.h>
#include <stdlib.h>
typedef int ElementType;
typedef struct Node *PtrToNode;
struct Node {
    ElementType Data;
    PtrToNode   Next;
};
typedef PtrToNode List;
List Read(); /* 细节在此不表 */
void Print( List L ); /* 细节在此不表;空链表将输出NULL */
List Merge( List L1, List L2 );
int main()
{
    List L1, L2, L;
    L1 = Read();
    L2 = Read();
    L = Merge(L1, L2);
    Print(L);
    Print(L1);
    Print(L2);
    return 0;
}
/* 你的代码将被嵌在这里 */

Sample Input

3
1 3 5
5
2 4 6 8 10

Sample Output

1 2 3 4 5 6 8 10
NULL
NULL

理解

这题起先是不会如何在合并完后将L1L2清空,然后后来发现只要循环插入到一个结束另一个直接接上,然后在刚开始定义时新的链表=L1L2,最后直接->next指空就好

AC代码

List Merge( List L1, List L2 ){
	List a,b,c,x; 
	x = (List)malloc(sizeof(struct Node));
	a = L1 -> Next; //L1 和 L2 头指针指向空结点
	b = L2 -> Next;
	c = x; //就相当于c是tail,x是head 
	while(a && b){
		if(a -> Data <= b -> Data){
			c -> Next = a;
			c = a;
			a = a -> Next; 
		}
		else{
			c -> Next = b;
			c = b;
			b = b -> Next; 
		}
	}
	//当a和b有一个空了 
	c -> Next = a ? a : b;
	//令L1L2为NULL 
	L1 -> Next = NULL;
	L2 -> Next = NULL;
	return x;
}

读入输出细节不表的代码

void Print(List L){
//	此函数的作用:打印链表中的元素 
    List P=L->Next;
 if(P!=NULL){
       List r;
       r = L;
       while(r->Next){
           r = r->Next;
           printf("%d ",r->Data);
       }
   }
   else{
        printf("NULL");
   }
   printf("\n");
}; 
List Read(){
//	此函数的作用:创建链表并插入数据
	int n;
	scanf("%d",&n);
//	输入该链表中元素的个数
    List L=(List)malloc(sizeof(PtrToNode));
//    申请一个头节点
    L->Next=NULL;
//    先建立一个以L为头结点的空链表 
    if(n!=0){
    	List r=L;
//    尾指针指向头结点 
		for(int i=0;i<n;i++){
			List p=(List)malloc(sizeof(struct Node));
			scanf("%d",&(p->Data));
			p->Next=NULL;
			r->Next=p;
			r=p;	
		}  
		r->Next=NULL;
	}
	return L;
};

(三)我的感想(不太星)

关于这周的日常练习

第三周也就刷刷水题然后并查集写了一些简单题,,很颓了,第四周撞上国庆假期也没有好好刷题。

关于开始划水的课堂

划水,划不得!链表好难得说(STL不香吗!)香啊,但是期末要考链表函数题,这让我怎么偷鸡莫!好好读树才是王道

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值