http://codeup.cn/contest.php?cid=100000605
题目描述:读入一个只包含 +, -, *, / 的非负整数计算表达式,计算该表达式的值。
30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92
0
12178.21
要注意这里采用的输入方法,以后可能会用到。
题目描述:请写一个程序,判断给定表达式中的括号是否匹配,表达式中的合法括号为”(“, “)”, “[", "]“, “{“, ”}”,这三个括号可以按照任意的次序嵌套使用。
4
[(d+f)*{}]
[(2+3))
()}
[4(6]7)9
yes
no
no
no
这个题目还是蛮简单的,就是个平衡符号的判断题。
#include
#include
#include
using namespace std;
int main() {
int n;
scanf("%d", &n);
getchar(); //吸收整数输入后的换行符
map<char, char> mp;
mp[']'] = '[';
mp[')'] = '(';
mp['}'] = '{';
char str[10000];
stack<char> s;
while (n--) {
while (!s.empty()) s.pop(); //清空循环外面定义的栈
gets(str);
int flag = 1; //符号是平衡的
for (int i = 0; str[i]; i++) {
if (str[i] == '[' || str[i] == '(' || str[i] == '{') s.push(str[i]);
else if (str[i] == ']' || str[i] == ')' || str[i] == '}') {
if (s.empty() || s.top() != mp[str[i]]) {
//此时栈空或栈顶开放符号不对应封闭符号
flag = 0; break;
} else s.pop();
}
} //输入处理结束后,栈非空,则报错
if (!s.empty() || !flag) printf("no\n");
else printf("yes\n");
}
return 0;
}
http://codeup.cn/contest.php?cid=100000606
相同的题目。
#include
#include
using namespace std;
void input(int num[]) {
for (int i = 0; i < 10; i++) {
scanf("%d", &num[i]);
}
}
void process(int num[]) {
int max, min;
max = min = 0;
for (int i = 0; i < 10; i++) {
if (num[i] < num[min]) min = i;
}
swap(num[0], num[min]);
for (int i = 0; i < 10; i++) {
if (num[i] > num[max]) max = i;
}
swap(num[9], num[max]);
}
void print(int num[]) {
for (int i = 0; i < 10; i++) {
printf("%d ", num[i]);
}
}
int main() {
int num[10];
input(num);
process(num);
print(num);
return 0;
}
http://codeup.cn/contest.php?cid=100000607
这种使用输出型引用参数和使用status表示函数执行状态的想法可以用在自己写数据结构的时候,还有这里使用头插法倒序插入元素创建链表的写法可以学习。
#include
#include
#define ERROR (0)
#define OK (1)
typedef int status;
typedef int ElemType;
typedef struct LNode {
ElemType data;
struct LNode *next;
} LNode, *LinkList;
status GetELem_L(LinkList &L, int i, ElemType &e) {
LinkList p = L->next;
int j = 1;
while (p && j < i) {
p = p->next;
j++;
}
if (!p || j > i) {
return ERROR;
}
e = p->data;
return OK;
}
status ListInsert_L(LinkList &L, int i, ElemType e) {
LinkList p = L, s;
int j = 0;
while (p && j < i - 1) {
p = p->next;
++j;
}
if (!p || j > i - 1) return ERROR;
s = (LinkList)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
status ListDelete_L(LinkList &L, int i, ElemType &e) {
LinkList p = L, q;
int j = 0;
while (p->next && j < i - 1) {
p = p->next;
j++;
}
if (!(p->next) || j > i - 1) return ERROR;
q = p->next;
p->next = q->next;
e = q->data;
free(q);
return OK;
}
void CreateList_L(LinkList &L, int n) {
LinkList p;
int i;
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
for (i = n; i > 0; --i) {
p = (LinkList)malloc(sizeof(LNode));
scanf("%d", &p->data);
p->next = L->next;
L->next = p; //插入到表头
}
}
void PrintList(LinkList &L) {
LinkList p = L->next;
int cnt = 0;
while (p) {
if (cnt++ > 0) printf(" ");
printf("%d", p->data);
p = p->next;
}
printf("\n");
}
int main() {
int n;
scanf("%d", &n);
LinkList L;
CreateList_L(L, n);
int m, a, e;
scanf("%d", &m);
char s[10];
while (m--) {
scanf("%s", s);
switch(s[0]) {
case 'd':
scanf("%d", &a);
if (ListDelete_L(L, a, e)) printf("delete OK\n");
else printf("delete fail\n");
break;
case 'g':
scanf("%d", &a);
if (GetELem_L(L, a, e)) printf("%d\n", e);
else printf("get fail\n");
break;
case 'i':
scanf("%d%d", &a, &e);
if (ListInsert_L(L, a, e)) printf("insert OK\n");
else printf("insert fail\n");
break;
case 's':
if (L->next == NULL) printf("Link list is empty\n");
else PrintList(L);
break;
}
}
return 0;
}
我没有用链表,用了两个数组分别排序,然后归并输出。
#include
#include
using namespace std;
struct stu {
int id, grade;
} a[1000], b[1000];
bool cmp(struct stu a, struct stu b) {
return a.id < b.id;
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) scanf("%d%d", &a[i].id, &a[i].grade);
for (int i = 0; i < m; i++) scanf("%d%d", &b[i].id, &b[i].grade);
sort(a, a + n, cmp); sort(b, b + m, cmp);
int j = 0, k = 0;
while (j < n && k < m) {
if (a[j].id < b[k].id) {
printf("%d %d\n", a[j].id, a[j].grade);
j++;
}
else if (a[j].id > b[k].id) {
printf("%d %d\n", b[k].id, b[k].grade);
k++;
}
}
for (; j < n; j++) printf("%d %d\n", a[j].id, a[j].grade);
for (; k < m; k++) printf("%d %d\n", b[k].id, b[k].grade);
return 0;
}
这样写确实麻烦了,干脆只用一个数组,全部输入,然后排序直接输出,代码如下。
#include
#include
using namespace std;
struct stu {
int id, grade;
} a[2000];
bool cmp(struct stu a, struct stu b) {
return a.id < b.id;
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i < n + m; i++) scanf("%d%d", &a[i].id, &a[i].grade);
sort(a, a + n + m, cmp);
for (int j = 0; j < n + m; j++) printf("%d %d\n", a[j].id, a[j].grade);
return 0;
}
正统的写法如下(:
#include
typedef struct LNode {
int id, grade;
struct LNode *next;
} LNode, *LinkList;
/* 找到id小于data的最后一个结点, 即data理论上的前一个结点, 通过pre返回内容 */
void FindPrevious(LinkList &L, int data, LinkList &pre) {
LinkList p = L;
while (p->next && p->next->id < data) p = p->next;
pre = p;
}
/* 通过输入和合适的插入创建n个元素的有序链表 */
void CreateSortedList(LinkList &L, int n) {
L = new LNode;
L->next = NULL;
LinkList p, pre;
for (int i = 0; i < n; i++) {
p = new LNode;
p->next = NULL;
scanf("%d%d", &p->id, &p->grade);
FindPrevious(L, p->id, pre);
p->next = pre->next;
pre->next = p; //插入到合适的位置
}
}
void PrintList(LinkList &L) {
LinkList p = L->next;
int cnt = 0;
while (p) {
printf("%d %d\n", p->id, p->grade);
p = p->next;
}
}
int main() {
int n, m;
while (scanf("%d%d", &n, &m) != EOF) {
LinkList L;
CreateSortedList(L, n + m);
PrintList(L);
}
return 0;
}
欺骗自己(完全使用数组?)的做法:
#include
int main() {
int n, m, a[1000];
while (scanf("%d", &n) != EOF) {
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
scanf("%d", &m);
for (int i = n; i < n + m; i++) scanf("%d", &a[i]);
for (int j = 0; j < n + m; j++) {
if (j > 0) printf(" ");
printf("%d", a[j]);
}
printf("\n");
}
return 0;
}
正统的写法如下:
这个题目使得我困惑, 线性表(a1,a2,a3,…,an)中元素递增有序且按顺序存储于计算机内
,这真的不是让我们用数组吗?虽然用数组插入删除效率低。 用最少时间在表中查找数值为x的元素,若找到……若找不到将则其插入表中并使表中元素仍递增有序
,这不是用二分查找吗?
先是数组的写法:
#include
#include
using namespace std;
int binarySearch(int a[], int n, int key) {
int low = 0, high = n - 1, mid;
while (low <= high) {
mid = (low + high) / 2;
if (key == a[mid]) return mid;
else if (key < a[mid]) high = mid - 1;
else if (key > a[mid]) low = mid + 1;
}
return -1;
}
int main() {
int x, n;
while (scanf("%d%d", &x, &n) != EOF) {
int a[n + 1], flag = 0;
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
/* 用最少时间在表中查找数值为x的元素 */
int pos = binarySearch(a, n, x);
/* 若存在则标记 */
if (pos != -1) {
/* 不为最后一个元素, 则将其与后继元素位置相交换 */
if (pos != n - 1) swap(a[pos], a[pos + 1]);
flag = 1;
} else if (pos == -1) {
/* 不存在则将其插入表中并使表中元素仍递增有序 */
int p;
for (p = n - 1; a[p] > x; p--) a[p + 1] = a[p];
a[p + 1] = x;
}
int len = flag ? n : n + 1;
if (!flag) printf("no\n");
for (int i = 0; i < len; i++) {
if (i > 0) printf(" ");
printf("%d", a[i]);
}
printf("\n");
}
return 0;
}
正统的写法如下:
有的人说输出“printf("list is empty");”无换行,否则会输出超限60%。
不过我试了,可以通过。
第一个方法是数组反转:
#include
int main() {
int n;
while (scanf("%d", &n) != EOF) {
if (n == 0) printf("list is empty\n");
else {
int a[n];
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
for (int i = 0; i < n; i++) {
if (i > 0) printf(" ");
printf("%d", a[i]);
}
printf("\n");
for (int i = n - 1; i >= 0; i--) {
if (i < n - 1) printf(" ");
printf("%d", a[i]);
}
printf("\n");
}
}
return 0;
}
第二个方法是头插法建表,直接输出,连反转都不用了:
正统的写法如下:
第一个方法是数组,其中删除重复元素的方法为双指针,慢指针用来保留元素,高速指针用来跳过重复元素。
#include
int main() {
int n;
while (scanf("%d", &n) != EOF) {
if (n == 0) printf("list is empty\n");
else {
int a[n];
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
//输出删除前的元素
for (int i = 0; i < n; i++) {
if (i > 0) printf(" ");
printf("%d", a[i]);
}
printf("\n");
int low = 0, high = 0;
while (high < n) {
a[low] = a[high++];
while (high < n && a[high] == a[low]) high++;
low++;
}
//输出删除后的元素
for (int i = 0; i < low; i++) {
if (i > 0) printf(" ");
printf("%d", a[i]);
}
printf("\n");
}
}
return 0;
}
正统的写法如下:
http://codeup.cn/contest.php?cid=100000608
题目描述:排列与组合是常用的数学方法。
3
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
使用next_permutation版本:
#include
#include
using namespace std;
int main() {
int n; scanf("%d", &n);
int a[n];
for (int i = 1; i <= n; i++) a[i - 1] = i;
do {
for (int i = 0; i < n; i++) {
if (i > 0) printf(" ");
printf("%d", a[i]);
}
printf("\n");
} while (next_permutation(a, a + n));
return 0;
}
自己写DFS枚举的版本:
#include
int n, hashTable[11] = {0}; //看看每个数选了没有
void printPermutation(int index, int a[]) { //index: 1-n当前需要确定位置的第几个元素
if (index > n) {
for (int i = 0; i < n; i++) {
if (i > 0) printf(" ");
printf("%d", a[i]);
}
printf("\n");
} else {
for (int i = 1; i <= n; i++) {
if (hashTable[i] == 0) {
hashTable[i] = 1;
a[index - 1] = i; //从0位开始填充
printPermutation(index + 1, a);
hashTable[i] = 0;
}
}
}
}
int main() {
scanf("%d", &n);
int a[n];
printPermutation(1, a);
return 0;
}
题目描述:排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r < = n),我们可以简单地将n个元素理解为自然数1,2,…,n,从中任取r个数。现要求你不用递归的方法输出所有组合。
例如n = 5 ,r = 3 ,所有组合为:
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
递归写法:保持组合中的元素的字典序,是为了防止出现顺序不同但是组合元素一样的相同组合。可以作为枚举排列剪枝为组合的条件。因此,可以知道,组合的第一个元素只能枚举到n-r+1为止。循环的方法懒得写。
#include
int n, r;
int hash[25] = {0};
void Combine(int index, int a[]) {
if (index > r) {
for (int i = 0; i < r; i++) {
if (i > 0) printf(" ");
printf("%d", a[i]);
}
printf("\n");
return;
}
for (int i = 1; i <= n; i++) {
if (hash[i] == 0) {
hash[i] = 1; //剪枝
//选组合开头第一个元素且其不大于…时; 后面的元素都大于前面的元素
if ((index == 1 && i <= n - r + 1) || i > a[index - 2]) {
a[index - 1] = i;
Combine(index + 1, a);
}
hash[i] = 0;
}
}
}
int main() {
scanf("%d%d", &n, &r);
int a[r];
Combine(1, a);
return 0;
}
题目描述:已知 n 个整数b1,b2,…,bn以及一个整数 k(k<n)。从 n 个整数中任选 k 个整数相加,可分别得到一系列的和。
例如当 n=4,k=3,4 个整数分别为 3,7,12,19 时,可得全部的组合与它们的和为:
3+7+12=22 3+7+19=29 7+12+19=38 3+12+19=34。
现在,要求你计算出和为素数共有多少种。例如上例,只有一种的和为素数:3+7+19=29。
4 3
3 7 12 19
1
#include
#include
int n, k;
int a[25], cnt = 0, sum = 0;
bool isPrime(int n) {
if (n <= 1) return false;
int sqr = sqrt(1.0 * n);
for (int i = 2; i <= sqr; i++)
if (n % i == 0) return false;
return true;
}
void Combine(int pos, int level) {
if (level == k) {
if (isPrime(sum)) cnt++;
return;
}
for (int i = pos; i < n; i++) {
sum += a[i];
Combine(i + 1, level + 1);
sum -= a[i];
}
}
int main() {
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
Combine(0, 0);
printf("%d\n", cnt);
return 0;
}
题目描述 会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题。
4
2 4 1 3
3 1 4 2
这是一个排列生成问题。如果视作子集问题或者组合问题,那么复杂度就太高了。
#include
int cnt = 0, n, hashTable[11] = {false}, col[10] = {0};
void dfs(int curRow){
if (curRow > n) {
for (int i = 1; i <= n; i++) {
if (i > 1) printf(" ");
printf("%d", col[i]);
}
cnt++;
printf("\n");
} else {
for (int x = 1; x <= n; x++) { //列数选择
if (hashTable[x] == false) { //第x列没有皇后
bool flag = true;
for (int pre = 1; pre < curRow; pre++) {
if (abs(curRow - pre) == abs(x - col[pre])) {
flag = false;
break;
}
}
if (flag) {
hashTable[x] = true;
col[curRow] = x;
dfs(curRow + 1);
hashTable[x] = false;
}
}
}
}
}
int main() {
scanf("%d", &n);
dfs(1);
if (cnt == 0) printf("no solute!\n");
return 0;
}
http://codeup.cn/contest.php?cid=100000610
http://codeup.cn/contest.php?cid=100000615
题目描述:某市计划建设一个通信系统。按照规划,这个系统包含若干端点,这些端点由通信线缆链接。消息可以在任何一个端点产生,并且只能通过线缆传送。每个端点接收消息后会将消息传送到与其相连的端点,除了那个消息发送过来的端点。如果某个端点是产生消息的端点,那么消息将被传送到与其相连的每一个端点。
为了提高传送效率和节约资源,要求当消息在某个端点生成后,其余各个端点均能接收到消息,并且每个端点均不会重复收到消息。
现给你通信系统的描述,你能判断此系统是否符合以上要求吗?
4 3
1 2
2 3
3 4
3 1
2 3
0 0
Yes
No
这题还是有点坑的,虽然这里说了没有将某个端点与其自己相连的线缆
,这就排除了环(自回路)。两个端点之间至多由一条线缆直接相连
,这就排除了多重边。但是它要求每个端点均不会重复收到消息
,而且0<=M<=N*(N-1)/2
,说明最大可以是无向完全图,每个端点都与其他端点有连线。而我们只想要树。根据树的边只有N-1条,可以判断会不会形成回路。
#include
const int maxn = 1010;
typedef int SetName;
typedef int ELemType;
typedef ELemType DisjSet[maxn];
DisjSet syst; //通信系统
void Initialize(int N) {
for (int i = 0; i < N; i++) syst[i] = -1;
}
SetName Find(ELemType x) {
if (syst[x] < 0) return x;
else return syst[x] = Find(syst[x]);
}
void SetUnion(SetName root1, SetName root2) {
if (syst[root1] < syst[root2]) {
syst[root1] += syst[root2];
syst[root2] = root1;
} else {
syst[root2] += syst[root1];
syst[root1] = root2;
}
}
int main() {
int n, m;
while (scanf("%d%d", &n, &m), n || m) {
Initialize(n);
ELemType a, b;
for (int i = 0; i < m; i++) {
scanf("%d%d", &a, &b);
SetName r1 = Find(a - 1), r2 = Find(b - 1);
if (r1 != r2) SetUnion(r1, r2); //两个端点间有连线
}
int ans = 0; //记录连通块个数
for (int i = 0; i < n; i++) {
if (syst[i] < 0) ans++;
}
if (ans == 1 && m == n - 1) printf("Yes\n");
else printf("No\n");
}
return 0;
}
题目描述:某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
5 3
1 2
3 2
4 5
0
1
与算法笔记上的代码有点相似。这题的输入允许多重边,甚至是回路,不过我们的代码自动剔除了这种情况。只需要统计出有多少个集合(划分)就可以了,最少修建的道路就是这些集合间的生成树,数目就是集合数-1。
#include
#include
const int maxn = 1010;
int father[maxn];
int Find(int x) {
if (father[x] < 0) return x;
else return father[x] = Find(father[x]);
}
void Union(int root1, int root2) {
if (father[root1] < father[root2]) {
father[root1] += father[root2];
father[root2] = root1;
} else {
father[root2] += father[root1];
father[root1] = root2;
}
}
int main() {
int n, m, a, b;
while (scanf("%d", &n) && n) {
memset(father, -1, sizeof(father));
scanf("%d", &m);
for (int i = 0; i < m; i++) {
scanf("%d%d", &a, &b);
int r1 = Find(a - 1), r2 = Find(b - 1);
if (r1 != r2) Union(r1, r2);
}
int ans = 0;
for (int i = 0; i < n; i++) {
if (father[i] < 0) ans++;
}
printf("%d\n", ans - 1); //集合数-1
}
return 0;
}
http://codeup.cn/contest.php?cid=100000616
题目描述:在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有3种果子,数目依次为1,2,9。可以先将 1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为 12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。
10
3 5 1 7 6 4 2 5 4 1
120
通过每次将最小的两堆搬到一起,N堆要搬运log N次,可以得到最小的体力消耗。其实就是哈夫曼树的构建思想。
#include
using namespace std;
priority_queue<long long, vector<long long>, greater<long long> > q;
int main() {
int n;
long long t;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%lld", &t);
q.push(t); //将初始重量压入优先队列
}
long long ans = 0; //体力最小消耗量
while (q.size() > 1) {
int t1 = q.top(); q.pop();
int t2 = q.top(); q.pop();
q.push(t1 + t2); //取出堆顶的两个元素, 求和后压入优先队列
ans += (t1 + t2); //累计求和的结果
}
printf("%lld\n", ans);
return 0;
}
本小节暂无练习题。
本小节暂无练习题。
但是图的存储和遍历与二叉树的存储与遍历都是基础中的基础。
该题的目的是要你统计图的连通分支数。
1 4
4 3
5 5
2
这一题确实有点坑,开始一看觉得很简单,也确实简单,不过它没有给出结点编号的范围。因此很容易运行错误
。下面的总结写的很完善了,引用过来。
事实上测试数据结点编号在1~1e6之间,标记数组稍微小一个数量级都会出现段错误
然后,就是要注意会出现自己指向自己的边,在使用邻接表进行记录时要区分一下,不然会有重复元素,但实际上不区分,也是可以通过的,就是耗时多一些。
访问顶点,直接在输入数据的时候就保存一个最大编号的变量,这样直接for循环就可以,省事而且高效。
————————————————
版权声明:本文为CSDN博主「漫浸天空的雨色」的原创文章。原文链接:https://blog.csdn.net/a845717607/article/details/81708837
#include
#include
using namespace std;
const int maxv = 500010;
vector<int> Adj[maxv];
bool vis[maxv] = {false};
int CreateGraph() {
int i, j;
int max = -1; //记录最大顶点的编号, 以便逐个访问连通块
while (scanf("%d%d", &i, &j) != EOF) {
if (i != j) {
Adj[i].push_back(j);
Adj[j].push_back(i);
} else Adj[i].push_back(j);
//i可能等于j
max = max > i ? max : i;
max = max > j ? max : j;
}
return max;
}
void DFS(int u) {
vis[u] = true; //点u已经访问
for (int i = 0; i < Adj[u].size(); i++) {
int v = Adj[u][i];
if (vis[v] == false) {
DFS(v);
}
}
}
void DFSTraverse(int &comp, int max) {
for (int i = 1; i <= max; i++) {
if (Adj[i].size() > 0 && vis[i] == false) { //该点存在
DFS(i); comp++; //访问结束了一个连通块
}
}
}
int main() {
int max = CreateGraph();
int components = 0;
DFSTraverse(components, max);
printf("%d\n", components); //打印连通分支个数
return 0;
}
题目描述:给定一个无向图和其中的所有边,判断这个图是否所有顶点都是连通的。
输入
每组数据的第一行是两个整数 n 和 m(0<=n<=1000)。n 表示图的顶点数目,m 表示图中边的数目。如果 n 为 0 表示输入结束。随后有 m 行数据,每行有两个值 x 和 y(0
输出
对于每组输入数据,如果所有顶点都是连通的,输出"YES",否则输出"NO"。
4 3
4 3
1 2
1 3
5 7
3 5
2 3
1 3
3 2
2 5
3 4
4 1
7 3
6 2
3 1
5 6
0 0
YES
YES
NO
很多这种无向图连通的问题,可以转化为并查集解决。
#include
const int maxv = 1010;
int father[maxv];
void Initialize(int n) {
for (int i = 0; i < n; i++) father[i] = -1;
}
int Find(int x) {
if (father[x] < 0) return x;
else return father[x] = Find(father[x]);
}
void SetUnion(int r1, int r2) {
if (father[r1] < father[r2]) {
father[r1] += father[r2];
father[r2] = r1;
} else {
father[r2] += father[r1];
father[r1] = r2;
}
}
int main() {
int n, m;
while (scanf("%d", &n) != EOF && n != 0) {
int comp = 0, u, v;
Initialize(n);
scanf("%d", &m);
for (int i = 0; i < m; i++) {
scanf("%d%d", &u, &v);
int r1 = Find(u - 1), r2 = Find(v - 1);
if (r1 != r2) SetUnion(r1, r2);
}
for (int i = 0; i < n; i++) {
if (father[i] < 0) comp++;
}
if (comp == 1) printf("YES\n");
else printf("NO\n");
}
return 0;
}
DFS的邻接表代码。事实上,这题的结点个数<=1000,完全可以用邻接矩阵。
#include
#include
using namespace std;
const int maxv = 1010;
vector<int> adj[maxv];
bool vis[maxv] = {0};
void CreateGraph(int m) {
for (int i = 1; i <= m; i++) adj[i].clear();
int u, v;
for (int i = 1; i <= m; i++) {
scanf("%d%d", &u, &v);
if (u != v) {
adj[u].push_back(v);
adj[v].push_back(u);
} else adj[u].push_back(v);
}
}
void DFS(int u) {
vis[u] = true;
for (int i = 0; i < adj[u].size(); i++) {
int v = adj[u][i];
if (vis[v] == false) {
DFS(v);
}
}
}
void DFSTrave(int n, int &comp) {
for (int i = 1; i <= n; i++) vis[i] = false;
for (int i = 1; i <= n; i++) {
if (vis[i] == false) { //孤立点也是一个连通块
DFS(i); comp++;
}
}
}
int main() {
int n, m;
while (scanf("%d", &n) != EOF && n != 0) {
int comp = 0;
scanf("%d", &m);
CreateGraph(m); //需要初始化邻接表
DFSTrave(n, comp); //每次遍历要重新设置vis数组
if (comp == 1) printf("YES\n");
else printf("NO\n");
}
return 0;
}