[SCOI2008]城堡

题目

题目背景
2008NOI四川省选

题目描述
在一个国家里,有n个城市(编号为0 到n-1)。这些城市之间有n条双向道

路相连(编号为0 到n-1),其中编号为i的道路连接了城市i和城市ri(一条道

路可以连接一个城市和它自身),长度为di。n 个城市中有m个拥有自己城堡,

可以抵御敌人侵略。如果没有城堡的城市遭受攻击,则离它最近的城堡将派兵前

往救援。

你的任务是在不超过k个没有城堡的城市中建立城堡,使得所有城市中“离

最近城堡的距离”的最大值尽量小。换句话说,若令dist©表示城市c的最近城

堡离它的距离,则你的任务是让max{dist©}尽量小。

输入数据保证存在方案使得对于每个城市,至少有一个城堡能够到达。

输入格式
输入第一行为三个正整数n, m, k。第二行包含n个整数r0,r1,…,rn-1。第三行

包含n 个整数d0,d1,…,dn-1。第四行包含m 个各不相同的0~n-1 之间的整数,分

别为m个城堡所在的城市编号。

输出格式
输出仅一行,包含一个整数,即max{dist©}的最小值。

输入输出样例
输入 #1复制
5 0 1
1 2 3 4 0
1 1 1 1 1
输出 #1复制
2
输入 #2复制
3 1 1
1 2 0
1 2 3
2
输出 #2复制
1
输入 #3复制
2 1 1
0 1
1 1
1
输出 #3复制
0
输入 #4复制
10 3 3
0 2 0 0 2 2 8 3 8 7
10 9 1 8 1 3 7 2 8 1
3 4 6
输出 #4复制
3
输入 #5复制
2 0 1
1 0
5 10
输出 #5复制
5
说明/提示
100%的数据满足:2<=n<=50, 1<=di<=106, 0<=m<=n-k

思路

dij套模拟退火

代码

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
long long dis[101][101];
int cnt[101],vis[101],ans,p[101],son[101];
void find(int mid)
{int i,j,Max,maxi;
	memset(cnt,0,sizeof(cnt));
	for (i=1;i<=n;i++)
		if (vis[i]==0)
			{
		for (j=1;j<=n;j++)
			if (dis[i][j]<=mid) 
				cnt[j]++;
			}
	Max=0,maxi=0;
	for (i=1;i<=n;i++)
		if (cnt[i]>=Max)
			{
		Max=cnt[i];
		maxi=i;
			}
	if (maxi==0) return;
	for (i=1;i<=n;i++)
		if (dis[maxi][i]<=mid) vis[i]=1;
}
bool check(int mid)
{int i,j;
	memset(vis,0,sizeof(vis));
	for (i=1;i<=m;i++)
		{
			for (j=1;j<=n;j++)
		if (dis[p[i]][j]<=mid) vis[j]=1;
		}
	for (i=1;i<=k;i++)
		find(mid);
	for (i=1;i<=n;i++)
		if (vis[i]==0) return 0;
	return 1;
}
int main()
{int i,j;
	long long d;
	cin>>n>>m>>k;
	memset(dis,127/2,sizeof(dis));
	for (i=1;i<=n;i++)
		{
			scanf("%d",&son[i]);
			son[i]++;
		}
	int l=0,r=0;
	for (i=1;i<=n;i++)
		{
			scanf("%lld",&d);
			dis[i][son[i]]=min(dis[i][son[i]],d);
			dis[son[i]][i]=min(dis[i][son[i]],d);
			r+=d;
		}
	for (l=1;l<=n;l++)
	for (i=1;i<=n;i++)
		if (i!=l)
		{
			for (j=1;j<=n;j++)
		if (l!=j&&i!=j)
		{
			dis[i][j]=min(dis[i][j],dis[i][l]+dis[l][j]);
		}
		}
	for (i=1;i<=n;i++)
		dis[i][i]=0;
	for (i=1;i<=m;i++)
		scanf("%d",&p[i]),p[i]++;
	l=0;
	while (l<=r)
		{
			int mid=(l+r)/2;
			if (check(mid)) ans=mid,r=mid-1;
			else l=mid+1;
		}
	cout<<ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值