牛的旅行(acwing和洛谷)

由于洛谷和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;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值