【JZOJ】【匈牙利算法】【二分】 导弹

题意

有一张 k k k* k k k的地图,地图上有 k k k个城市,地图上的 i , j i,j i,j表示第 i i i个城市到第 j j j个城市的距离,地图上有两个国家, A A A& B B B A A A国有 n n n个城市,每个城市有一枚导弹, B B B国有 m m m个城市,一枚导弹能摧毁一个 B B B国的城市,求 A A A国要多久才能摧毁整个 B B B

样例

输入
3
0 2 1
2 0 10
1 10 0
1
2
1
3
输出
3

样例解释

0 	2	1
2	0	10
1	10   0	

第二行第三列=10,表示编号为 2 ( A 2(A 2A国的城市 ) ) 的城市到编号为 3 ( B 3(B 3B国的城市 ) ) 的直接距离为 10 10 10,但这个距离并不是最优的,我们通过观察可以发现,从 A A A国出发(编号为 2 2 2的城市),先到编号为 1 1 1的城市(用时为 2 2 2),再从这个城市出发到 B B B国城市,用时为 1 1 1
所以摧毁 B B B国最快的时间 = 2 + 1 = 3 =2+1=3 =2+1=3
所以样例答案为 3 3 3

思路

首先通过Floyd求出最短路径,然后二分+最大匹配求出答案就OK了。

代码

#include<iostream>
#include<cstring> 
#include<cstdio>

using namespace std;

int f[505][505];
int t, N, M, K, ans;
int h[105], used[105], link[105], A[105], B[105];

struct node
{
	int to, next;
}w[5005];

void add(int x, int y)
{w[++t] = (node){y, h[x]}; h[x] = t;}

bool find(int x)
{
	for(int i = h[x]; i; i = w[i].next)
	{
		int to = w[i].to;
		if (!used[to])
		{
			int p = link[to];
			link[to] = x;
			used[to] = 1;
			if (!p || find(p)) return 1;
			link[to] = p;
		}
	}
	return 0;
}

bool check(int x)
{
	int sum = 0;
	t = 0;
	memset(w, 0, sizeof(w));
	memset(h, 0, sizeof(h));
	memset(link, 0, sizeof(link));
	for (int i = 1; i <= N; ++i)
	for (int j = 1; j <= M; ++j)
		if (f[A[i]][B[j]] <= x) add(i, j);
	for (int i = 1; i <= N; ++i) {
		memset(used, 0, sizeof(used));
		if (find(i)) sum++;
	}
	return sum == M;
}

int main()
{
	memset(f, 0x7f, sizeof(f));
	scanf("%d", &K);
	for (int i = 1; i <= K; ++i)
	for (int j = 1; j <= K; ++j)
		scanf("%d", &f[i][j]);
	for (int i = 1; i <= K; ++i)
	for (int j = 1; j <= K; ++j)
	for (int k = 1; k <= K; ++k)
		f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
	scanf("%d", &N);
	for (int i = 1; i <= N; ++i)
		scanf("%d", &A[i]);
	scanf("%d", &M);
	for (int i = 1; i <= M; ++i)
		scanf("%d", &B[i]);
	int l = 1, r = 1e6;
	while (l <= r)
	{
		int mid = (l + r) / 2;
		if (check(mid)) r = mid - 1, ans = mid;
		else l = mid + 1;
	}
	printf("%d", ans);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值