Tree
Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2676 Accepted Submission(s): 830
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.
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).
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个点,每个点都有一个值。如果两个点a, b之间满足a或者b或者a+b是素数,那么这两个点之间有一条路径,路径的权值为min(min(a, b), |a-b|)。求最小生成树。
解析:最小生成树问题,用prim和Kruskal都行,不过需要根据给定的点的值进行预处理建图。
代码(prime):
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <cstring>
#include <cmath>
using namespace std;
#define N 605
#define INF 1000000000
typedef long long ll;
ll mp[N][N], vi[N], dis[N];
bool vis[N];
bool tag[2000005];
int n;
void Prime()
{
memset(tag,0,sizeof(tag));
tag[0]=tag[1]=1;
for(int i=2; i*i <= 2000001; i++)
if(tag[i]==0){
for(int j=i+i;j<=2000001;j+=i)
tag[j]=1;
}
}
ll Prim(){
for(int i = 0; i < n; i++){
dis[i] = mp[0][i];
vis[i] = false;
}
int st = 0;
ll ans = 0;
dis[st] = 0;
vis[st] = true;
int ct = n - 1, flag = 1;
while(ct && flag){
ct--;
int Min = INF;
flag = 0;
for(int i = 0; i < n; i++){
if(!vis[i] && Min > dis[i]){
Min = dis[i];
st = i;
flag = 1;
}
}
ans += Min;
vis[st] = true;
for(int i = 0; i < n; i++){
if(st == i)continue;
if(!vis[i] && dis[i] > mp[st][i])
dis[i] = mp[st][i];
}
}
if(flag == 0)return -1;
return ans;
}
int main(){
int t;
scanf("%d", &t);
Prime();
while(t--){
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%lld", &vi[i]);
for(int i = 0; i < n; i++){
for(int j = i; j < n; j++){
if(i == j)mp[i][j] = 0;
else if(!tag[vi[i]] || !tag[vi[j]] || !tag[vi[i]+vi[j]])
mp[i][j] = mp[j][i] = min(min(vi[i], vi[j]), abs(vi[i]-vi[j]));
else
mp[i][j] = mp[j][i] = INF;
}
}
ll ans = Prim();
printf("%lld\n", ans);
}
return 0;
}
代码(Kruskal):
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define N 605
#define INF 1000000000
typedef long long ll;
struct node{
int start, end;
ll len;
bool operator < (const node &a)const{
return len < a.len;
}
};
node Edge[N*N/2];
ll vi[N], dis[N];
bool tag[2000005];
int n, ecnt;
int father[N];
void Prime()
{
memset(tag,0,sizeof(tag));
tag[0]=tag[1]=1;
for(int i=2; i*i <= 2000001; i++)
if(tag[i]==0){
for(int j=i+i;j<=2000001;j+=i)
tag[j]=1;
}
}
int getFather(int x){
return x == father[x]? x : father[x] = getFather(father[x]);
}
bool Join(int a, int b){
int fa = getFather(a);
int fb = getFather(b);
if(fa != fb){
father[fa] = fb;
return true;
}
return false;
}
ll Kruskal(){
for(int i = 0; i < n; i++){
father[i] = i;
}
ll ans = 0;
int cnt = n - 1;
for(int i = 0; i < ecnt; i++){
if(Join(Edge[i].start, Edge[i].end)){
cnt--;
ans += Edge[i].len;
}
}
if(cnt > 0)return -1;
return ans;
}
int main(){
int t;
node p;
scanf("%d", &t);
Prime();
while(t--){
scanf("%d", &n);
ecnt = 0;
for(int i = 0; i < n; i++)
scanf("%lld", &vi[i]);
for(int i = 0; i < n; i++){
for(int j = i; j < n; j++){
if(i == j)continue;
if(!tag[vi[i]] || !tag[vi[j]] || !tag[vi[i]+vi[j]]){
ll len = min(min(vi[i], vi[j]), abs(vi[i]-vi[j]));
Edge[ecnt].start = i, Edge[ecnt].end = j, Edge[ecnt++].len = len;
}
}
}
sort(Edge, Edge+ecnt);
ll ans = Kruskal();
printf("%lld\n", ans);
}
return 0;
}
因为这个图可能存在多个集合之间没有路径,即不存在最小生成树,需要判断。