首页 > 其他分享 >[POJ1734]Sightseeing 观光之旅 题解

[POJ1734]Sightseeing 观光之旅 题解

时间:2022-12-10 19:33:07浏览次数:70  
标签:POJ1734 get int 题解 pos back text path Sightseeing

无向图的最小环问题,前置芝士:\(\text{Floyd}\)

传送门

题目描述

给定一张无向图,求图中一个至少包含 \(3\) 个点的环,环上的节点不重复,并且环上的边的长度之和最小。

你需要输出最小环的方案,若最小环不唯一,输出任意一个均可。

想法

拿到题会想到找出所有的环,然后进行统计。

可是对于无向图而言,要找到具体的环路不是一件易事。

于是考虑如何不找环。

思路

看到数据范围:

\(1≤N≤100, 1≤M≤10000\)

点数这么少,相当于直接明示用 \(O(n^3)\) 的\(\text{Floyd}\) 算法。

整个图里跑完一遍

先看 \(\text{Floyd}\) 的核心代码:

for(int k = 1; k <= n; k ++)
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= n; j ++)
            f[i][j] = min(f[i][k], f[k][j]);

这个算法以 \(k\) 为中继点,\(i, j\) 作为附加状态,进行 \(dp\) 地状态转移。

可以发现,在最外层的循环刚开始的时候,f[i][j] 表示在经过编号 \(k - 1\) 的点的情况下,\(i \rightarrow j\) 的最小值。

我们不妨把它的过程画出来,一下是某张图的一部分。

image

那么在不经过编号 \(\geq k\) 的点的情况下,\(f[i][j]\) 一定就指的是下面的 \(i \rightarrow ... \rightarrow j\) 的路径长度。

于是,发现 \(\min\limits_{1\leq i < j < k} f[i][j] + g[i][k] + g[k][j]\) 即满足以下条件的一个最小环的长度:

  • 经过的点编号都不超过 \(k\);
  • 经过点 \(i, j, k\),且 \(k\) 处于 \(i, j\) 中间并只经过一条边。

对于任意的 \(k\in [1,n]\),都可以在 \(O(n^2)\) 的时间内计算出他所对应的所有环的长度,于是我们只需要取一个最小值即可。

而对于输出答案的限制,我们可以用一个数组 pos[i][j] 来储存在 \(\text{Floyd}\) 过程中 \(i\) 与 \(j\) 最短路径的中继点 \(k\)。

于是我们可以进行递归处理。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#define INF 0x3f3f3f3f

using namespace std;

const int N = 110, M = 2e4 + 10;

int d[N][N], g[N][N], pos[N][N];

vector<int> path;
int n, m;

void init()
{
    memset(pos, 0, sizeof pos);
    memset(g, 0x3f, sizeof g);
    for(int i = 1; i <= n; i ++) g[i][i] = 0;
    cin >> n >> m;
    for(int i = 1; i <= m; i ++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        g[a][b] = g[b][a] = min(g[a][b], c); // 避免重边
    }
    memcpy(d, g, sizeof g);
}

void get(int x, int y)
{
    if(x == y || !pos[x][y]) return ; // pos[x][y] == 0表示 x, y 之间没有任何点
    get(x, pos[x][y]); // 递归左边
    path.push_back(pos[x][y]); // 中间(当前点)
    get(pos[x][y], y); // 右边
}

signed main()
{
    init();

    int ans = INF;
    for(int k = 1; k <= n; k ++)
    {
        for(int i = 1; i < k; i ++)
        {
            for(int j = i + 1; j < k; j ++)
            {
                if(ans > (long long)d[i][j] + g[i][k] + g[k][j]) // 枚举k的邻点,如果i或j没有到达k的边,那么右式必然大于INF,不会考虑
                {
                    ans = d[i][j] + g[i][k] + g[k][j];
                    path.clear();
                    path.push_back(i); // 按照i -> ... -> j -> k的顺序存环,这题有spj,别急
                    get(i, j);
                    path.push_back(j);
                    path.push_back(k);
                }
            }
        }
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= n; j ++)
                if(d[i][j] > d[i][k] + d[k][j]) // 更新f数组
                    d[i][j] = d[i][k] + d[k][j],
                    pos[i][j] = k; // 记录i, j的中继点k
    } 

    if(ans == INF) puts("No solution."); // 无解情况
    else 
    {
        for(auto i : path) // 输出方案
            cout << i << ' ';
    }

    return 0;
}

双倍经验,输出 ans 即可。

标签:POJ1734,get,int,题解,pos,back,text,path,Sightseeing
From: https://www.cnblogs.com/MoyouSayuki/p/16972162.html

相关文章

  • 问题解决系列:io.lettuce.core.RedisCommandExecutionException_ CLUSTERDOWN
    问题场景程序调用​​redis​​集群,总是间歇性地提示报错,报错提示如下:org.springframework.data.redis.RedisSystemException:Errorinexecution;nestedexceptionisio......
  • P2018:消息传递题解——二次扫描与换根
    消息传递题面题目描述巴蜀国的社会等级森严,除了国王之外,每个人均有且只有一个直接上级,当然国王没有上级。如果A是B的上级,B是C的上级,那么A就是C的上级。绝对不会出现这样......
  • FJ的农场 题解
    原题见P4216首先\(\Theta(mn)\)暴力能够拿到\(30\)分,这个没有什么难度,可以参照一下我用来测试的暴力Link。首先让我们来简化一下题意:插入操作(即"\(Grow\)"),将树......
  • AcWing2437. Splay 题解
    题目大意:splay执行区间翻转示例程序:#include<bits/stdc++.h>usingnamespacestd;constintmaxn=1e5+5;intn,m;structNode{ints[2],p,v;intsz,flag......
  • 洛谷P8767 [蓝桥杯 2021 国 A] 冰山 题解 splay tree
    题目链接:​​https://www.luogu.com.cn/problem/P8767​​鸣谢:这道题的顺利解决得到了​​7KByte​​大佬的大力帮助,在此再次表示感谢。首先,我的想法是这样的:使用一个spl......
  • 【题解】P8866 [NOIP2022] 喵了个喵(构造,adhoc)
    【题解】P8866[NOIP2022]喵了个喵题目链接P8866[NOIP2022]喵了个喵题意概述有一个牌堆和\(n\)个可以从栈底删除元素的栈,任务是要通过规则将所有的卡牌消去。开......
  • #yyds干货盘点#按钮点击重复提交问题解决
    提交按钮重复点击这是最常见的问题,重复提交会造成多条数据入库。点击提交给个loading提示过渡,期间按钮不可再次触发就可以。​查询按钮重复点击如果查询按钮点一下就设置loa......
  • 题解 CF1713F【Lost Array】
    首先,为了方便将第\(1\)行的数从右往左重标号为\(0,1,\cdots,n-1\)。我们发现\((1,i)\)对于\((j,n+1)\)的贡献是\(C(i+j,i)\pmod2\),根据\(\text{lucas}......
  • P8377 [PFOI Round1] 暴龙的火锅 题解
    题目传送门题目背景暴龙爱吃火锅。题目描述定义\(S(x)\)表示\(x\)的每一位的数字之和,例如:\(S(14)=1+4=5\),\(S(114514)=1+1+4+5+1+4=16.\)另外,定义\(fib(x)\)代......
  • CF1458C 题解
    题意传送门\(T\)组测试数据,每次给一个\(n\timesn\)的矩阵,每行每列都是个\(1\ton\)的排列。有\(m\)次操作,如果是UDLR就是要把整个矩阵每行/每列往一个方向循环......