由于洛谷和acwing所求描述不同,题意有所改变
1125. 牛的旅行(acwing)
1125. 牛的旅行
思路
生成的新牧场和原牧场的最大直径
1.floyd算法求出任意两个连通点之间的距离
2.求出所有连通集合中的点到该点最远的距离
3.枚举所有非连通点**maxd[i] + maxd[j] + dist[i,j] **
即i点在该连通集合中最远的距离 + j点在该连通集合中最远的距离 + i两点的j的实际距离
4.在原牧场和新牧场取最大值即可
样例输入:
8
10 10
15 10
20 10
15 15
20 15
30 15
25 10
30 10
01000000
10111000
01001000
01001000
01110000
00000010
00000101
00000010
样例输出:
22.071068
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef pair<double, double> PDD;
const int N = 205, INF = 1e9;
int g[N][N], n;
double dist[N][N];
double maxx[N];
PDD c[N];
double get(double x1, double y1, double x2, double y2)
{
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
int main()
{
scanf("%d", &n);
memset(dist, 0x3f, sizeof dist);
for(int i = 1; i <= n; i ++ )
{
double a, b;
scanf("%lf%lf", &a, &b);
c[i] = {a, b};
}
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= n; j ++ )
{
scanf("%1d", &g[i][j]);
dist[i][j] = INF;
}
for(int i = 1; i <= n; i ++ ) dist[i][i] = 0;
//将坐标转换为实即距离
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= n; j ++ )
if(g[i][j])
dist[i][j] = min(dist[i][j], get(c[i].first, c[i].second, c[j].first, c[j].second));
//Flody求联通块中的联通点之间的最短路
for(int k = 1; k <= n; k ++ )
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= n; j ++ )
{
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
g[i][j] |= g[i][k] & g[k][j];
}
//求在联通块中距离该点的最远距离,并记录两个联通块的最长距离
double ress = 0;
for(int i = 1; i <= n; i ++ )
{
for(int j = 1; j <= n; j ++ )
if(g[i][j])
maxx[i] = max(maxx[i], dist[i][j]);
ress = max(ress, maxx[i]);
}
//求非联通块之间点与点之间的实即距离 + 各自联通块的距离该点的最长距离
double res = INF;
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= n; j ++ )
if(!g[i][j] && i != j)
{
res = min(res, get(c[i].first, c[i].second, c[j].first, c[j].second) + maxx[i] + maxx[j]);
}
printf("%.6lf\n", max(res, ress));
return 0;
}
P1522 [USACO2.4] 牛的旅行 Cow Tours
此处的题意和acwing上不同的是求新生成牧场的最长直径
新生成牧场得最大值分为3种
1.A连通块中的直径
2.B连通块中的直径
3.A连通块中距离点 i 的最长距离 + B联通块中距离点 j 的最长距离 + i 和 j的实际距离
此处分为3种的原因是通过3的所有情况取Min可能使得直径比原来A或B联通块的直径还要小
例如:
1.A联通块的直径为0
2.B联通块的直径为4
3.最小的直径为3
显然使得新生成的直径为3是不存在的
在每次求最小直径时需要在123中取max,因为就算直径在小也不可能比A联通块和B联通块的直径更小
代码需要在acwing的牛的基础上求每个联通块的直径:深度搜索遍历连通块中的所有点,直径为连通块中所有距离该点最远距离的最大值
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef pair<double, double> PDD;
const int N = 205, INF = 1e9;
int g[N][N], n;
double dist[N][N]; //Flody的最短距离
double maxx[N]; //距离该点的最远距离
double block[N]; //连通块
double ans;
PDD c[N];
int p[N], cnt;
double get(double x1, double y1, double x2, double y2)
{
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
void dfs(int u)
{
//将该点加入联通块集合中
p[u] = cnt;
//更新该连通块集合的直径
block[cnt] = max(block[cnt], maxx[u]);
//遍历所有能联通且未加入连通块的点
for(int i = 1; i <= n; i ++ )
{
if(!g[u][i] || p[i]) continue;
dfs(i);
}
return;
}
int main()
{
scanf("%d", &n);
memset(dist, 0x3f, sizeof dist);
for(int i = 1; i <= n; i ++ )
{
double a, b;
scanf("%lf%lf", &a, &b);
c[i] = {a, b};
}
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= n; j ++ )
{
scanf("%1d", &g[i][j]);
dist[i][j] = INF;
}
for(int i = 1; i <= n; i ++ ) dist[i][i] = 0;
//坐标转化为距离
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= n; j ++ )
if(g[i][j])
dist[i][j] = min(dist[i][j], get(c[i].first, c[i].second, c[j].first, c[j].second));
//Flody求连通块中所有能间接相连的最短距离
for(int k = 1; k <= n; k ++ )
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= n; j ++ )
{
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
g[i][j] |= (g[i][k] & g[k][j]);
}
for(int i = 1; i <= n; i ++ )
{
for(int j = 1; j <= n; j ++ )
{
if(g[i][j])
maxx[i] = max(maxx[i], dist[i][j]);
}
}
for(int i = 1; i <= n; i ++ )
{
//如果该点的连通块未加入某连通块集合
if(p[i]) continue;
//划分连通块
cnt ++;
//搜索与该点相连的点 即连通块
dfs(i);
}
double res = INF;
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= n; j ++ )
if(!g[i][j] && i != j)
{
res = min(res, max(get(c[i].first, c[i].second, c[j].first, c[j].second) + maxx[i] + maxx[j],max(block[p[i]], block[p[j]])));
}
printf("%.6lf\n", res);
return 0;
}