魔法森林

魔法森林

(forest.pas/c/cpp)

【题目描述】

亮亮在梦中游历了魔法城堡后,对此心驰神往,于是用自己制造的法杖,创造了一片魔法森林。

这片森林中一开始有 n 个节点,没有边相连,若想要在第 i 个点和第 j 个点之间建立一条双向通路,则需花费 Cij 的魔法值。

每个结点上住着一个魔法居民,若两个节点间有边直接相连,则他们就成为了邻居。居民一共有三种类型:

①村民:他们只能通过道路拜访自己的邻居。

②巫师:他们可以拜访自己的邻居以及邻居的邻居。

③大魔法师:由于他们拥有法力,因此可以拜访所有与自己连通的人。

亮亮不希望有人孤单,因此他保证了每种类型的居民要么不出现,否则至少出现两个。同时,他又希望大家能建立良好的关系,所以他决定花费魔法值为魔法森林修路,使得任意居民都可以拜访其他所有的居民。

他想知道,最少需要建立多少条道路才能达成自己的心愿。在道路数目最少的前提下,花费的魔法值最小又是多少。

【输入格式】

第一行有一个整数 n。

第二行有 n 个整数, 第 i 个整数表示 i 号节点所居住的居民的类型(1 表示村民, 2 表示巫师, 3 表示大魔法师) 。

接下来 n 行,每行 n 个数,是一个 n*n 的矩阵 Cij。数据保证 Cij = Cji, Cii=0。

【输出格式】

一行两个整数, 分别表示最小道路数和最小魔法值。

【样例输入】

3

1 1 1

0 1 2

1 0 3

2 3 0

【样例输出】

3 6

【数据规模】

对于 30%的数据,只存在大魔法师;

对于 100%的数据, 1 <= n <= 250, 0 <= Cij <= 10^9。

【问题分析】

是不是以为所有的第三题都是很难的题?这一题仔细想想就会发现,这完全是一个暴力题。

1. 有村民,除了村民本身之外,其他的所有人都要往村民上连一条边。

2. 没有村民,有巫师(格格巫),那这就是一个太阳图,就是枚举一个中心,然后所有点都往这个电上连边。不过有特殊的情况,就是有两个巫师,那可能有两个太阳,两个太阳间连一条边,然后其它点离哪个巫师近就往那个巫师上连边。

3. 只有魔法师,那就是一棵最小生成树。

#include<cstdio>

#include<iostream>

#include<algorithm>

#define MAX 0x3f3f3f3f

using namespace std;

typedef long long lnt;

const int N=255;

struct dist

{

    int x,y;lnt di;

    bool operator < (const dist&a)

    const {return di<a.di;}

}s[N*N];

lnt dis,d[N][N];

int n,a[N],fa[N],g,ans,t[4];

bool f[N][N];

void vill()

{

    for(int i=1;i<=n;i++)if(a[i]==1)

    for(int j=1;j<=n;j++)

    if(!f[i][j]&&i!=j)ans++,

    dis+=d[i][j],

    f[i][j]=1,f[j][i]=1;

    printf("%d %I64d\n",ans,dis);

}

void gegewu()

{

    ans=n-1;dis=MAX;dis*=MAX;dis*=5;

    for(int i=1;i<=n;i++)

    {

        lnt dig=0;

        for(int j=1;j<=n;j++)

        if(i!=j)dig+=d[i][j];

        if(dig<dis)dis=dig;

    }if(t[2]==2)

    {

        for(int i=1;i<=n;i++)if(a[i]==2)

        for(int j=i+1;j<=n;j++)if(a[j]==2)

        {

            lnt dig=d[i][j];

            for(int k=1;k<=n;k++)

            if(k!=i&&k!=j)

            dig+=min(d[k][i],d[k][j]);

            if(dig<dis)dis=dig;

        }

    }printf("%d %I64d\n",ans,dis);

}

int find(int a)

{

    if(fa[a]!=a)

    fa[a]=find(fa[a]);

    return fa[a];

}

void klu()

{

    for(int i=1;i<=n;i++)fa[i]=i;

    for(int i=1;i<=n;i++)

    for(int j=i+1;j<=n;j++)

    if(i!=j)s[++g]=(dist){i,j,d[i][j]};

    sort(s+1,s+g+1);ans=n-1;

    for(int i=1;i<=g;i++)

    {

        int fx=find(s[i].x);

        int fy=find(s[i].y);

        if(fa[fx]!=fa[fy])

        fa[fx]=fa[fy],n--,

        dis+=s[i].di;

        if(n<=1) break;

    }printf("%d %I64d\n",ans,dis);

}

int main()

{

    freopen("forest.in","r",stdin);

    freopen("forest.out","w",stdout);

    scanf("%d",&n);

    for(int i=1;i<=n;i++)

    scanf("%d",&a[i]),t[a[i]]++;

    for(int i=1;i<=n;i++)

    for(int j=1;j<=n;j++)

    scanf("%I64d",&d[i][j]);

    if(t[1]) vill();

    if((!t[1])&&t[2])gegewu();

    if((!t[1])&&(!t[2]))klu();

    return 0;

}
View Code

 

posted @ 2020-03-12 21:33  Johnny-English  阅读(386)  评论(0编辑  收藏  举报