间谍派遣 题解

题目

题目描述

        你是M,一个雇佣N个标号为从1到N的间谍的情报机关的总管。每个间谍被派往不同的国家并在那获取重要情报。

如下是你的任务:

        1.在部分间谍间组织会面。每次会面在两个间谍间进行,两个间谍交换他们自己获取的或从之前会面中得到的信息。因为在不同国家的两个间谍间组织机密会面很困难,所以每次秘密会面都有一个费用。

        2.当所有会面结束后,选择一部分间谍参加拯救世界的任务。一个间谍k参加此项任务需要花费Mk。很重要的一点是,任务要成功,必须满足参加任务的间谍获取的情报聚集到一起包含了其他剩余间谍所有的信息。
        请找出完成任务的最小花费,即组织会面和派遣间谍的费用之和。

输入

输入的第一行包含正整数N,表示间谍数目(2≤N≤1000)。

接下来的N行包含N个不超过10^6的非负整数。在k行m列的数字表示间谍k和m间会面的花费,同样的,与m行k列的数字相等,k=m时数字为0。

接下来的一行包含N个正整数Mk(1≤Mk≤10^6),分别为派遣间谍1,2,…,N参加任务的花费。

输出

只有一行,为所需的最小总费用。

样例输入

输入1:
3
0 6 9
6 0 4
9 4 0
7 7 7
输入2:
3
0 17 20
17 0 10
20 10 0
15 9 12
输入3:
5
0 3 12 15 11
3 0 14 3 20
12 14 0 11 7
15 3 11 0 15
11 20 7 15 0
5 10 10 10 10

样例输出

输出1:
17
输出2:
34
输出3:
28

题目大意

        有n个人,你要让他们通过最少的花钱把情报聚集在几个人身上,然后花最少的钱让这几个人去拯救世界。

数据结构

        这一道题,输入都输入了个邻接矩阵,不用图还能用啥?

算法

        他说要你花最少的钱把情报聚集在几个人身上,那么求的肯定是最小生成树。

        我们先试试把他都集中到一个人身上(样例1):

        我们选取(2,3)和(1,2)这两条边,然后让1去拯救世界,总费用是6+4+7=17,对了。

        我们再来看样例2:

         用上述算法,得出答案是17+10+15=42,但正确答案是34更小,因为他让(2,3)会面,让1和2去拯救世界,这样总费用是10+15+9=34。

        因此得出结论:有时候让更多人去拯救世界比让一个人更优。

        那怎么解决这个问题呢?我们只需要多连n条边,把每个节点都连向0,权值是让他拯救世界的费用,然后我们再做最小生成树。

之所以可以这样做,是因为拯救世界和会面用的费用的单位是一样的,所以它们都可以做边权。学过网络流的都知道这个知识点。

代码(Kruskal)

#include <cstdio>
using namespace std;

struct node
{
	int x;
	int y;
	int v;
};

void swap(node &a,node &b);
const int N=7000005;
int m=0;
int a[N],fa[N];
node e[N];

void add(int x,int y,int z)
{
	m++;
	e[m].x=x;
	e[m].y=y;
	e[m].v=z;
}

int find(int k)
{
	if (k!=fa[k]) fa[k]=find(fa[k]);
	return fa[k];
}

void qsort(int l,int r)
{
	int i=l,j=r;
	int key=e[l].v,t;
	while (i<=j)
	{
		while (e[i].v<key) i++;
		while (e[j].v>key) j--;
		if (i<=j)
		{
			swap(e[i],e[j]);
			i++,j--;
		}
	}
	if (l<j) qsort(l,j);
	if (i<r) qsort(i,r);
}

int main()
{
	int n,ans=0;
	scanf("%d",&n);
	for (int i=1; i<=n; i++) fa[i]=i;
	for (int i=1; i<=n; i++)
		for (int j=1; j<=n; j++)
		{
			int z;
			scanf("%d",&z);
			if (z!=0) add(i,j,z);
		}
	for (int i=1; i<=n; i++)
	{
		scanf("%d",&a[i]);
		add(i,0,a[i]);
	}
	qsort(1,m);
	for (int i=1; i<=m; i++)
	{
		int fx=find(e[i].x),fy=find(e[i].y);
		if (fx!=fy)
		{
			ans+=e[i].v;
			fa[fx]=fy;
		}
	}
	printf("%d",ans);
	return 0;
}

void swap(node &a,node &b)
{
	node t;
	t=a,a=b,b=t;
}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值