题目:
题目描述
亮亮在梦中游历了魔法城堡后,对此心驰神往,于是用自己制造的法杖,创造了一片魔法森林。
这片森林中一开始有 n 个节点,没有边相连,若想要在第 i 个点和第 j 个点之间建立一条双向通路,则需花费 Cij 的魔法值。
每个结点上住着一个魔法居民,若两个节点间有边直接相连,则他们就成为了邻居。居民一共有三种类型:
①村民:他们只能通过道路拜访自己的邻居。 ②巫师:他们可以拜访自己的邻居以及邻居的邻居。
③大魔法师:由于他们拥有法力,因此可以拜访所有与自己连通的人。
亮亮不希望有人孤单,因此他保证了每种类型的居民要么不出现,否则至少出现两个。同时,他又希望大家能建立良好的关系,所以他决定花费魔法值为魔 法森林修路,使得任意居民都可以拜访其他所有的居民。
他想知道,最少需要建立多少条道路才能达成自己的心愿。在道路数目最少的前提下,花费的魔法值最小又是多少。
输入输出格式
输入格式:
第一行有一个整数 n。
第二行有 n 个整数,第 i 个整数表示 i 号节点所居住的居民的类型(1 表示 村民,2 表示巫师,3 表示大魔法师)。
接下来 n 行,每行 n 个数,是一个 n*n 的矩阵 Cij。数据保证Cij = Cji, Cii=0。
输出格式:
一行两个整数,分别表示最小道路数和最小魔法值。
输入输出样例
输入样例#1:
3
1 1 1
0 1 2
1 0 3
2 3 0
输出样例#1:
3 6
说明
对于30%的数据,只存在大魔法师;
对于100%的数据,1 <= n <= 250,0 <= Cij <= 10^9。
思路:
分类讨论——
- 只有③,此时求最小生成树就好。
- 存在①,那么只需要满足①,这张图就会连通而满足③,且每两组②都会与至少一组①相连。
- 存在②,不存在①。
· 只有两个②,这种情况考试时没注意到,分两种情况考虑,一是这两个②直接相 连,其他的③选择路径较小的②相连,二是和下面的情况相同的考虑方法。
· 不只两个②,那么枚举每个节点作为中心构造菊花图就好。
代码:
#include<bits/stdc++.h>
using namespace std;
#define read(x) scanf("%lld",&x)
#define maxn 250
#define maxm 250*250
#define ll long long
struct Edge {
ll x,y,z;
Edge(){}
Edge(ll xx,ll yy,ll zz) {
x=xx,y=yy,z=zz;
}
bool operator < (const Edge& oth) const {
return z<oth.z;
}
};
ll n;
ll w[maxn+5];
ll c[maxn+5][maxn+5];
ll fa[maxm+5];
vector<Edge> e;
bool bld[maxn+5][maxn+5];
ll find(ll x) {
if(fa[x]) return fa[x]=find(fa[x]);
return x;
}
ll kruskal() {
ll s=0;
for(ll i=1;i<=n;i++) for(ll j=i+1;j<=n;j++) e.push_back(Edge(i,j,c[i][j]));
sort(e.begin(),e.end());
for(ll i=0;i<e.size();i++) {
ll fa1=find(e[i].x),fa2=find(e[i].y);
if(fa1==fa2) continue;
fa[fa1]=fa2;
s+=e[i].z;
}
return s;
}
int main() {
read(n);
for(ll i=1;i<=n;i++) read(w[i]);
for(ll i=1;i<=n;i++) for(ll j=1;j<=n;j++) read(c[i][j]);
for(ll i=1;i<=n;i++) if(w[i]!=3) goto NOTONLY;
printf("%lld %lld",n-1,kruskal());
return 0;
NOTONLY:;
bool flg=0;
ll ans=0,s=0;
int w2=0;
int id1=0,id2=0;
for(ll i=1;i<=n;i++)
if(w[i]==1) {
flg=true;
for(ll j=1;j<=n;j++) {
if(i!=j&&!bld[i][j]&&!bld[j][i]) {
bld[i][j]=true;
ans+=c[i][j];
s++;
}
}
} else if(w[i]==2) {
w2++;
if(id1) id2=i;
else id1=i;
}
if(flg) {
printf("%lld %lld",s,ans);
return 0;
}
if(w2==2) {
ll sum=0;
sum+=c[id1][id2];
for(int i=1;i<=n;i++) {
if(w[i]==3) {
sum+=min(c[i][id1],c[i][id2]);
}
}
s=sum;
}else s=1e16;
for(ll i=1;i<=n;i++) {
ll sum=0;
for(ll j=1;j<=n;j++) sum+=c[i][j];
if(sum<s) s=sum,ans=i;
}
printf("%lld %lld",n-1,s);
return 0;
}