题意:图中有n个点,每个点都有一个值。如果两个点a, b之间满足a或者b或者a+b是素数,那么这两个点之间有一条路径,路径的权值为min(min(a, b), |a-b|)。求最小生成树。
解析:最小生成树问题,用prim和Kruskal都行,不过需要根据给定的点的值进行预处理建图。
代码(prime):
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
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;
}
因为这个图可能存在多个集合之间没有路径,即不存在最小生成树,需要判断。