[SCOI2008]城堡
题目描述
在一个国家里,有n个城市(编号为0 到n-1)。这些城市之间有n条双向道
路相连(编号为0 到n-1),其中编号为i的道路连接了城市i和城市ri(一条道
路可以连接一个城市和它自身),长度为di。n 个城市中有m个拥有自己城堡,
可以抵御敌人侵略。如果没有城堡的城市遭受攻击,则离它最近的城堡将派兵前
往救援。
你的任务是在不超过k个没有城堡的城市中建立城堡,使得所有城市中“离
最近城堡的距离”的最大值尽量小。换句话说,若令dist(c)表示城市c的最近城
堡离它的距离,则你的任务是让max{dist(c)}尽量小。
输入数据保证存在方案使得对于每个城市,至少有一个城堡能够到达。
输入输出格式
输入格式:
输入第一行为三个正整数n, m, k。第二行包含n个整数r0,r1,…,rn-1。第三行
包含n 个整数d0,d1,…,dn-1。第四行包含m 个各不相同的0~n-1 之间的整数,分
别为m个城堡所在的城市编号。
输出格式:
输出仅一行,包含一个整数,即max{dist(c)}的最小值。
输入输出样例
说明
100%的数据满足:2<=n<=50, 1<=di<=106, 0<=m<=n-k
先存图,直接用floyd求出最短路
继续二分最大长度mid,对于每个已有城堡的城市,直接去标记其能到达的城市
然后对于不能到达的
我们将其距离不超过枚举的mid的点期望+1,分别在k次中每次找到最大期望的值进行建城堡。
有个玄学:在比较找出最大期望相同时要找编号尽量大的?????
复杂度O(n^3+logd*k*n^2)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 int n,m,k; 7 long long dis[101][101]; 8 int cnt[101],vis[101],ans,p[101],son[101]; 9 void find(int mid) 10 {int i,j,Max,maxi; 11 memset(cnt,0,sizeof(cnt)); 12 for (i=1;i<=n;i++) 13 if (vis[i]==0) 14 { 15 for (j=1;j<=n;j++) 16 if (dis[i][j]<=mid) 17 cnt[j]++; 18 } 19 Max=0,maxi=0; 20 for (i=1;i<=n;i++) 21 if (cnt[i]>=Max) 22 { 23 Max=cnt[i]; 24 maxi=i; 25 } 26 if (maxi==0) return; 27 for (i=1;i<=n;i++) 28 if (dis[maxi][i]<=mid) vis[i]=1; 29 } 30 bool check(int mid) 31 {int i,j; 32 memset(vis,0,sizeof(vis)); 33 for (i=1;i<=m;i++) 34 { 35 for (j=1;j<=n;j++) 36 if (dis[p[i]][j]<=mid) vis[j]=1; 37 } 38 for (i=1;i<=k;i++) 39 find(mid); 40 for (i=1;i<=n;i++) 41 if (vis[i]==0) return 0; 42 return 1; 43 } 44 int main() 45 {int i,j; 46 long long d; 47 cin>>n>>m>>k; 48 memset(dis,127/2,sizeof(dis)); 49 for (i=1;i<=n;i++) 50 { 51 scanf("%d",&son[i]); 52 son[i]++; 53 } 54 int l=0,r=0; 55 for (i=1;i<=n;i++) 56 { 57 scanf("%lld",&d); 58 dis[i][son[i]]=min(dis[i][son[i]],d); 59 dis[son[i]][i]=min(dis[i][son[i]],d); 60 r+=d; 61 } 62 for (l=1;l<=n;l++) 63 for (i=1;i<=n;i++) 64 if (i!=l) 65 { 66 for (j=1;j<=n;j++) 67 if (l!=j&&i!=j) 68 { 69 dis[i][j]=min(dis[i][j],dis[i][l]+dis[l][j]); 70 } 71 } 72 for (i=1;i<=n;i++) 73 dis[i][i]=0; 74 for (i=1;i<=m;i++) 75 scanf("%d",&p[i]),p[i]++; 76 l=0; 77 while (l<=r) 78 { 79 int mid=(l+r)/2; 80 if (check(mid)) ans=mid,r=mid-1; 81 else l=mid+1; 82 } 83 cout<<ans; 84 }