WEEK6作业-C-掌握魔法的东东

题目
东东在老家农村无聊,想种田。农田有 n 块,编号从 1~n。种田要灌溉

众所周知东东是一个魔法师,他可以消耗一定的 MP 在一块田上施展魔法,使得黄河之水天上来。他也可以消耗一定的 MP 在两块田的渠上建立传送门,使得这块田引用那块有水的田的水。 (1<=n<=3e2)

黄河之水天上来的消耗是 Wi,i 是农田编号 (1<=Wi<=1e5)

建立传送门的消耗是 Pij,i、j 是农田编号 (1<= Pij <=1e5, Pij = Pji, Pii =0)

东东为所有的田灌溉的最小消耗

Input
第1行:一个数n
第2到第n+1行:数wi
第n+2行到第2n+1行:矩阵即pij矩阵
Output
东东最小消耗的MP值

Example
Input
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
Output
9

思路:
如果没有黄河之水天上来,那么很显然就是一个最小生成树的问题,但是这道题也能转化位最小生成树的问题,我们假设有一个0号农田,它到每一个农田的传送门消耗的MP就是黄河之水天上来的MP,这样就变成了n+1个点的最小生成树问题,用kruskal算法,每次取权值最小的边加入,如果这个边能够生成环那就舍弃,直到边为n条。怎么判断能否生成环呢——并查集找边的端点是否已经都在边集里面。

代码:

#include<iostream>
#include<algorithm>
using namespace std;
int par[305]; 
int rak[305];
struct Edge{
 int u;
 int v;
 int w;
}edge[50000];
int tot=0;
void addEdge(int u,int v,int w)
{
 edge[tot].u=u;
 edge[tot].v=v;
 edge[tot].w=w;
 //edge[tot].nxt=head[u];
 //head[u]=tot;
 tot++;
}
int cmp(Edge a,Edge b)
{
 return a.w<b.w;
}
void init(int n)
{
 for(int i=0;i<=n;i++)
 {
  par[i]=i;
  rak[i]=1;
 }
}
int find(int x)
{
 if(par[x]==x)
 return x;
 else
 return par[x]=find(par[x]);
}
bool unite(int x,int y)
{
 x=find(x);
 y=find(y);
 if(x==y)
 return false;
 if(rak[x]>rak[y])
 swap(x,y);
 par[x]=y;
 rak[y]+=rak[x];
 return true;
}
void kruskal()
{
 long long MP=0;
 sort(edge,edge+tot,cmp);
 for (int i = 0;i<tot;i++)
 {
  int u= edge[i].u;
  int v= edge[i].v;
  int w=edge[i].w;
  if (unite(u, v))
   MP+=w;
 }
 cout<<MP<<endl;
}
int main()
{
 int n;
 cin>>n;
 init(n);
 for(int i=1;i<=n;i++)
 {
  int w;
  cin>>w;
  addEdge(0,i,w);
 }
 for(int i=1;i<=n;i++)
 for(int j=1;j<=n;j++)
 {
  int p;
  cin>>p;
  if(j>i)
  addEdge(i,j,p);
 }
 kruskal();
 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值