2015编程之美初赛1 hihoCoder1158 质数相关 树形DP

29 篇文章 1 订阅
19 篇文章 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


思路 筛法求范围内所有质数。枚举集合内任意两个数,求它俩是否质数相关。是则加一条无向边,建图。这个图是一个无向的森林,这一点可以简单证明下:

若a1和a2有边连接到b,则有a1*p1 = a2 * p2 = b。不妨设a2 > a1。

假设a1和a2间还另有一条通路.那么有两种情况,一种存在c = a1*p3 = a2*p4。c/b 得到 p3/p1 = p4/p2 两个不同质数之比不会相等,因此矛盾。

另一种情况,a1 是 a2 的因数则有a1 * k = a2 ,那么有a1*k*p2 = a1*p1 得到k*p2 = p1 ,这与p1是质数矛盾。

因此a1 和 a2间无回。

因此可以进行树dp。因为我们要删去一些点,让森林中无边,因此父节点和它下层间最多有一层被保留,因此设计状态dp(i,j)表示i这棵子树,i节点被保留或不保留时的最多节点数。

//#include <bits/stdc++.h>   //国外OJ支持 
#include <iostream>
#include <stdio.h>
#include <cmath>
#include <cstdio>
#include <vector>
#include <queue>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdlib>
#include <map>
#include <assert.h>
using namespace std;
#define I64_MAX 9223372036854775807 
typedef long long ll;
const double pi=acos (-1.0);
const double eps=1e-8 ;
//const ll INF=(I64_MAX)/2;
//#pragma comment(linker, "/STACK:102400000,102400000")
const int inf=0x3f3f3f3f ;
#define maxx(a) memset(a, 0x3f, sizeof(a))
#define zero(a) memset(a, 0, sizeof(a))
#define FILL(a,b) memset(a, b, sizeof(a))
#define REP(i,a,b) for(i=a;i<b;i++)
#define rep(i,n) REP(i,0,n)
#define srep(i,n) for(i = 1;i <= n;i ++)
#define snuke(c,itr) for( __typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++)
#define MP make_pair
#define fi first
#define se second
typedef pair <int, int> PII;
typedef pair <ll, ll> PX;
typedef pair<int,ll> PIL;
#define MAX 500000
const int maxn = 1002;

int n,m;
bool is_prime[MAX+1];
int s[maxn];
int g[maxn];
int dp[maxn][2];

struct Edge{
	int u,v,next;
}edge[maxn<<1];

void addEdge(int u,int v){
	edge[m<<1].u = u;
	edge[m<<1].v = v;
	edge[m<<1].next = g[u];
	g[u] = (m<<1);
	edge[(m<<1)|1].v = u;
	edge[(m<<1)|1].u = v;
	edge[(m<<1)|1].next = g[v];
	g[v] = (m<<1)|1;
}

void sieve(){
	int n = MAX;
	for(int i=0;i<=n;i++){
		is_prime[i] = 1;
	}
	is_prime[0] = is_prime[1] = 0;
	for(int i=2;i<=n;i++){
		if(is_prime[i]==1){
			for(int j=2*i;j<=n;j+=i)
				is_prime[j] = false;
		}
	}
}

void dfs(int x){
	if(dp[x][0] != -1)
		return;
	dp[x][0] = 0;
	dp[x][1] = 1;
	for(int e=g[x];e!=-1;e=edge[e].next){
		int v =	edge[e].v;
		if(dp[v][0] != -1)
			continue;
		dfs(v);
		dp[x][0] += max(dp[v][0],dp[v][1]);
		dp[x][1] += dp[v][0];
	}
}



int main ()
{
    // freopen("E:\\input.txt" ,"r", stdin);
    //freopen ("E:\\out.txt","w",stdout);
	int i,j;
	int T,t;
	memset(is_prime,0,sizeof(is_prime));
	
	sieve();
	scanf("%d",&T);
	for(t=1;t<=T;t++)
	{
		m = 0;
		scanf("%d",&n);
		memset(g,-1,sizeof(g));
		memset(dp,-1,sizeof(dp));
		for(i=0;i<n;i++){
			scanf("%d",&s[i]);
		}
		sort(s,s+n);
		for(i=0;i<n;i++){
			for(j=i+1;j<n;j++){
				if(s[j] % s[i] != 0)
					continue;
				int tmp = s[j] / s[i];
				if(is_prime[tmp] == 1)
				{
					addEdge(i,j);
					m++;
				}
			}
		}
		int now = n;
		dp[n][0] = 0;
		for(i=0;i<n;i++){
			
			if(dp[i][0] == -1)
			{
				dfs(i);
				dp[n][0] += max(dp[i][0],dp[i][1]);
			}
		}
		printf("Case #%d: %d\n",t,max(dp[n][0],dp[n][1]));
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值