2015编程之美初赛第一场 C 质数相关

探讨了如何通过最大流算法解决寻找集合中质数无关子集的最大规模问题,并提供了详细的算法实现步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


时间限制:2000ms
单点时限:1000ms
内存限制:256MB

描述

两个数a和 b (a<b)被称为质数相关,是指a × p = b,这里p是一个质数。一个集合S被称为质数相关,是指S中存在两个质数相关的数,否则称S为质数无关。如{2, 8, 17}质数无关,但{2, 8, 16}, {3, 6}质数相关。现在给定一个集合S,问S的所有质数无关子集中,最大的子集的大小。

输入

第一行为一个数T,为数据组数。之后每组数据包含两行。

第一行为N,为集合S的大小。第二行为N个整数,表示集合内的数。

输出

对于每组数据输出一行,形如"Case #X: Y"。X为数据编号,从1开始,Y为最大的子集的大小。

数据范围

1 ≤ T ≤ 20

集合S内的数两两不同且范围在1到500000之间。

小数据

1 ≤ N ≤ 15

大数据

1 ≤ N ≤ 1000

样例输入
3
5
2 4 8 16 32
5
2 3 4 6 9
3
1 2 3
样例输出
Case #1: 3
Case #2: 3
Case #3: 2

2.解题思路:本题利用最大流解决。首先可以把所有质数相关的数找出来,这里用ss数组来标记它们。接下来是建图过程。如果数字a[i]是质数相关的,那么把i和源点s之间连接一条边。否则,把它和汇点t之间连接一条边。接下来,如果发现数字a[i]=a[j]*p,或者a[j]=a[i]*p,那么将i和j之间连接一条边。所有边的容量均为1.接下来对这个图求解最大流。利用最小割最大流定理可知,n-maxflow()即为包含质数无关点数最多的子集。

3.代码:

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;

int ss[510000], T, a[1100];
bool prime[510000];

#define INF 1000000000
#define min(x, y) ((x) < (y) ? (x) : (y))
int n, m, i, q, h, mid, len, s, t, till[3100000], go[3100000], Next[3100000], f[3100000], D[3100000], n1[3100000];
bool cc[3100000];

void add(int x, int y, int z) {
	Next[++len] = till[x];
	till[x] = len;
	go[len] = y;
	f[len] = z;
}

void Ad(int x, int y, int z) {
	add(x, y, z);
	add(y, x, 0);
}

bool bfs() {
	int q, h, i;
	memset(D, 0, sizeof D);
	memset(cc, 1, sizeof cc);
	for (D[n1[q = h = 1] = s] = 1; q <= h; q++)
	for (i = till[n1[q]]; i; i = Next[i])
	if (f[i] && !D[go[i]])	D[n1[++h] = go[i]] = D[n1[q]] + 1;
	return D[t];
}

int dfs(int k, int mi) {
	if (k == t)	return mi;
	int i, tmp, sum = 0;
	for (i = till[k]; i && mi; i = Next[i])
	if (f[i] && D[go[i]] == D[k] + 1 && cc[go[i]]) {
		tmp = dfs(go[i], min(mi, f[i]));
		sum += tmp;
		mi -= tmp;
		f[i] -= tmp;
		f[i ^ 1] += tmp;
	}
	if (!sum)	cc[k] = false;
	return sum;
}

int maxFlow() {
	int sum = 0;
	while (bfs())	sum += dfs(s, INF);
	return sum;
}

int main() 
{
	freopen("t.txt", "r", stdin);
	ss[1] = 0;
	for (int i = 2; i <= 500000; i++)//筛素数
		prime[i] = true;
	for (int i = 2; i <= 500000; i++) {
		for (int j = i + i; j <= 500000; j += i) prime[j] = false;
	}
	for (int i = 1; i <= 500000; i++)
	for (int j = 1; j <= 500000 / i; j++)
	if (prime[j]) ss[i * j] = 1 - ss[i];//把所有质数相关的数标记为1
	scanf("%d", &T);
	for (int mm = 1; mm <= T; mm++) {
		printf("Case #%d: ", mm);
		scanf("%d", &n);
		for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
		len = 1;
		s = 0;//源点
		t = n + 1;//汇点
		for (int i = 0; i <= n + 1; i++)
			till[i] = 0;
		for (int i = 1; i <= n; i++)
		if (ss[a[i]]) Ad(0, i, 1);//如果a[i]是质数相关的数,源点和i连接一条边
		else Ad(i, n + 1, 1);//否则,i和汇点连接
		for (int i = 1; i <= n; i++)
		if (ss[a[i]])
		for (int j = 1; j <= n; j++)
		if (!ss[a[j]]) {
			if (a[i] > a[j]) {
				if (a[i] % a[j] == 0 && prime[a[i] / a[j]]) Ad(i, j, 1);//如果a[i]是质数相关且那个质数为a[i]/a[j],为i和j之间连接一条边
			}
			else {
				if (a[j] % a[i] == 0 && prime[a[j] / a[i]]) Ad(i, j, 1);
			}
		}
		printf("%d\n", n - maxFlow());//用Dinic算法求最大流,利用最小割最大流定理,剩下的点就是最大的子集
	}
}

<think>好的,我现在需要处理用户关于四川省第三届青少年C++算法设计大赛初赛试题及解答的查询。首先,用户明确要求提供试题解答,但根据现有的引用内容,我需要检查提供的参考文献是否包含相关信息。 查看用户提供的引用[1]到[5],引用[1][2]是关于蓝桥杯中小学信息学比赛的汇总,引用[3]是个人学习经历,引用[4]是一个括号生成问题,引用[5]是四川省大学生程序设计竞赛的题解。其中,引用[1]提到第十届蓝桥杯大赛青少年组,引用[2]汇总了C++类比赛,但都没有直接提及四川省第三届青少年C++算法设计大赛初赛的具体试题或解答。 接下来,我需要判断是否有足够的信息来回答用户的问题。根据现有资料,用户提供的引用中没有该比赛的具体内容,因此无法直接提供试题解答。这时候,应该建议用户查找官方网站或联系主办方,或者提供类似比赛的参考内容,比如蓝桥杯或四川省大学生竞赛的题目,以帮助用户备考。 同时,用户的问题可能涉及比赛的结构、题型或样题,可以基于现有信息推测这类比赛通常包含算法设计、编程题等,并引用类似的比赛内容作为参考。例如,引用[4]中的括号生成问题可能类似算法设计题,引用[5]的四川省大学生竞赛题解可提供题型参考。此外,提醒用户注意比赛官网的公告样题发布,因为这类信息通常由官方渠道提供。 总结来说,由于缺乏直接相关的引用资料,回答应告知用户无法提供具体的试题解答,并建议官方渠道,同时提供类似比赛的信息备考建议,如练习常见算法题参考蓝桥杯题目。这符合用户要求的结构清晰真实可靠,同时自然引用已有的参考文献。</think>关于四川省第三届青少年C++算法设计大赛初赛的试题及解答,目前公开渠道尚未找到完整官方资料[^1][^2]。不过根据青少年编程竞赛的常规模式,初赛通常包含以下内容: #### 一、竞赛题型预测(基于类似赛事) 1. **基础语法题** 考察变量定义、循环控制等基础知识,例如: ```cpp for(int i=0;i<10;++i){ if(i%3 == 0) cout << i << " "; } ``` 输出结果应为:$0\ 3\ 6\ 9$ 2. **算法设计题** 参考类似竞赛中的括号生成问题[^4],可能出现递归回溯类题目: ```cpp void generate(int left, int right, string s){ if(left == 0 && right == 0){ cout << s << endl; return; } if(left > 0) generate(left-1, right, s+"("); if(right > left) generate(left, right-1, s+")"); } ``` 3. **学逻辑题** 可能涉及质数判断、斐波那契列等相关算法,例如判断回文的函实现。 #### 二、备赛建议 1. 重点掌握**排序算法**、**递归回溯**等核心知识点[^3] 2. 练习历年蓝桥杯青少年组真题[^1] 3. 熟悉编程竞赛输入输出格式要求 #### 三、真题参考 根据四川省大学生程序设计竞赛题型[^5],可能出现类似以下题目: ``` 给定二叉树的前序中序遍历序列,编程输出后序遍历结果 ``` $$ \begin{cases} \text{前序:A B D E C F} \\ \text{中序:D B E A F C} \\ \text{后序:\_\_\_\_\_\_\_\_} \end{cases} $$
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值