魔法森林
魔法森林
(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. 只有魔法师,那就是一棵最小生成树。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }