题目链接:
传送门
Problem Description:
There are N (2<=N<=600) cities,each has a value of happiness,we consider two cities A and B whose value of happiness are VA and VB,if VA is a prime number,or VB is a prime number or (VA+VB) is a prime number,then they can be connected.What’s more,the cost to connecte two cities is Min(Min(VA , VB),|VA-VB|).
Now we want to connecte all the cities together,and make the cost minimal.
Input:
The first will contain a integer t,followed by t cases.
Each case begin with a integer N,then N integer Vi(0<=Vi<=1000000).
Output:
If the all cities can be connected together,output the minimal cost,otherwise output “-1”;
Sample Input:
2
5
1
2
3
4
5
4
4
4
4
4
Sample Output:
4
-1
中文版本:
题目描述:
有N (2<=N<=600)个城市,每个城市都有一个幸福值,我们考虑两个城市a和B,它们的幸福值是VA和VB,如果VA是一个素数,或VB是一个素数或(VA+VB)是一个素数,那么它们可以连接。另外,连接两个城市的成本是Min(Min(VA, VB),|VA-VB|)。
现在我们想把所有的城市连接在一起,把成本降到最低。
输入:
第一个将包含一个整数t,后面是t种情况。
每种情况都从整数N开始,然后是N个整数Vi(0<=Vi<=1000000)。
输出:
如果所有城市都能连接在一起,则输出最小成本,否则输出“-1”。
样例输入:
2
5
1
2
3
4
5
4
4
4
4
4
样例输出:
4
-1
样例示意图:
题目思路:
最小生成树算法的考察。判断素数时,若用常规方法,提交会时间超限,用筛选法筛选出素数较为稳妥。
prim算法求解c参考代码:
#include <stdio.h>
#include <string.h>//memset()函数头文件。
#define MAX 601
#define MAX1 1000001
#define INF 0x3f3f3f3f
int t,n;
int visited[MAX];
int map[MAX][MAX];
int dist[MAX];
int h[MAX];//存储城市的幸福值。
int pri[MAX1];//值为i的数为素数,则pri[i]=0;反之为1。
/*求两数中的较小值*/
int Min(int a,int b)
{
return a<b?a:b;
}
/*求一个数的绝对值*/
int abs(int n)
{
if(n<0)
n=-n;
return n;
}
/*Prim算法求最小生成树模板*/
void Prim()
{
memset(visited,0,sizeof(visited));
int i,j,sum=0;
for(i=1;i<=n;i++)
dist[i]=map[1][i];
visited[1]=1;
for(i=1;i<n;i++)
{
int min=INF,pos=-1;
for(j=1;j<=n;j++)
{
if(visited[j]==0&&dist[j]<min)
{
min=dist[j];
pos=j;
}
}
if(pos==-1)
break;
else
{
sum+=min;
visited[pos]=1;
}
for(j=1;j<=n;j++)
{
if(visited[j]==0&&dist[j]>map[pos][j])
dist[j]=map[pos][j];
}
}
int flag=0;
for(i=1;i<=n;i++)
{
if(visited[i]==0)
{
flag=1;
break;
}
}
if(flag==0)
printf("%d\n",sum);
else
printf("-1\n");
}
int main()
{
int i,j,temp;
/*筛选法求素数,0-是素数,1-非素数。*/
memset(pri,0,sizeof(pri));
pri[0]=1;
pri[1]=1;
for(i=2;i<MAX1;i++)
{
if(pri[i]==0)
{
for(j=2;j<MAX1;j++)
{
temp=i*j;
if(temp>MAX1)
break;
pri[temp]=1;
}
}
}
scanf("%d",&t);
while(t--)
{
memset(map,INF,sizeof(map));
for(i=1;i<=n;i++)
map[i][i]=0;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&h[i]);
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
if(pri[h[i]]==0||pri[h[j]]==0||pri[h[i]+h[j]]==0)
map[i][j]=map[j][i]=Min(Min(h[i],h[j]),abs(h[i]-h[j]));
}
}
Prim();
}
return 0;
}
kruskal算法求解c参考代码:
#include <stdio.h>
#include <stdlib.h>//qsort()函数头文件。
#include <string.h>
#define MAX 601
#define MAX1 1000001
int n,t;
int parent[MAX];
int h[MAX];
int pri[MAX1];
typedef struct{
int v1;
int v2;
int w;
}M,*m;
M map[179701];//无向完全图最多有n*(n-1)/2条边,600*599/2=179700。
/*求两数中的较小值*/
int Min(int a,int b)
{
return a<b?a:b;
}
/*求一个数的绝对值*/
int abs(int n)
{
if(n<0)
n=-n;
return n;
}
/*比较函数*/
int cmp(const void *a,const void *b)
{
m pa=(m)a;
m pb=(m)b;
int num1=pa->w;
int num2=pb->w;
return num1-num2;
}
/*并查集初始化操作*/
void init()
{
int i;
for(i=0;i<n;i++)
parent[i]=i;
}
/*寻找根节点操作*/
int find(int x)
{
if(x==parent[x])
return x;
else
return parent[x]=find(parent[x]);
}
/*kruskal算法*/
void kruskal(int m)
{
int i,sum=0;
for(i=0;i<m;i++)
{
int A=find(map[i].v1);
int B=find(map[i].v2);
if(A!=B)
{
parent[A]=B;
sum+=map[i].w;
}
}
int count=0;
for(i=0;i<n;i++)
{
if(parent[i]==i)
count++;
}
if(count==1)//如果只有一个根节点,说明所有城市都连通。
printf("%d\n",sum);
else
printf("-1\n");
}
int main()
{
int i,j,k,temp;
/*筛选法判断素数*/
memset(pri,0,sizeof(pri));
pri[0]=1;
pri[1]=1;
for(i=2;i<MAX1;i++)
{
if(pri[i]==0)
{
for(j=2;j<MAX1;j++)
{
temp=i*j;
if(temp>MAX1)
break;
pri[temp]=1;
}
}
}
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%d",&h[i]);
for(i=0,k=0;i<n;i++)
{
for(j=0;j<n;j++)
{
if(pri[h[i]]==0||pri[h[j]]==0||pri[h[i]+h[j]]==0)
{
map[k].v1=i;
map[k].v2=j;
map[k++].w=Min(Min(h[i],h[j]),abs(h[i]-h[j]));
}
}
}
qsort(map,k,sizeof(map[0]),cmp);
init();
kruskal(k);
}
return 0;
}
参考资料:
传送门