码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-04-26 15:21:37      阅读:131      评论:0      收藏:0      [点我收藏+]

标签:最大流   图论   二分图   


时间限制: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算法求最大流,利用最小割最大流定理,剩下的点就是最大的子集
	}
}

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

标签:最大流   图论   二分图   

原文地址:http://blog.csdn.net/u014800748/article/details/45287773

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!