P1522:牛的旅行 Cow Tours 题解

主要思路

先用 Floyed 算出初始最短路,然后再枚举两个不在同一连通块内的点进行连边并且更新当前最短路然后用 DFS 求出最长链。这道题我的垃圾做法没有\(O2\)无法 AC,时间复杂度\(O(n^4)\)。(这可能是我写过最慢的代码了)

// P1522.cpp
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <vector>
#define pr pair<int, int>
#define dpr pair<double, int>
using namespace std;
const int MX_N = 200, INF = 0x3f3f3f3f;
int n;
char mat[MX_N][MX_N];
double G[MX_N][MX_N], ripe[MX_N][MX_N];
pr points[MX_N];
bool vis[MX_N];
double pow2(double num) { return num * num; }
double getDist(pr a, pr b) { return sqrt(pow2(a.first - b.first) + pow2(a.second - b.second)); }
double dfs(int u)
{
    if (vis[u])
        return 0;
    vis[u] = true;
    double res = 0;
    for (int i = 1; i <= n; i++)
        if (ripe[u][i] != INF && i != u)
            res = max(res, max(ripe[u][i], dfs(i)));
    return res;
}
int main()
{
    scanf("%d", &n);
    int x, y;
    for (int i = 1; i <= n; i++)
        scanf("%d%d", &x, &y), points[i] = make_pair(x, y);
    for (int i = 1; i <= n; i++)
    {
        scanf("%s", mat[i] + 1);
        for (int j = 1; j <= n; j++)
            if (mat[i][j] == '1')
                G[i][j] = getDist(points[i], points[j]);
            else if (i != j)
                G[i][j] = (double)INF;
    }
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                G[i][j] = min(G[i][j], G[i][k] + G[k][j]);
    double ans = (double)INF;
    for (int i = 1; i <= n; i++)
        for (int j = i + 1; j <= n; j++)
            if (i != j && G[i][j] == INF)
            {
                memcpy(ripe, G, sizeof(G));
                memset(vis, false, sizeof(vis));
                ripe[i][j] = ripe[j][i] = getDist(points[i], points[j]);
                for (int a = 1; a <= n; a++)
                    for (int b = 1; b <= n; b++)
                        ripe[a][b] = min(ripe[a][b], ripe[a][i] + ripe[i][j] + ripe[j][b]);
                double res = dfs(i);
                ans = min(ans, res);
            }
    printf("%.6f", ans);
    return 0;
}

 

P1841:「JSOI2007」重要的城市题解

思路

这道题是lornd巨佬给我写的。由于我太菜了,只能放弃看题解。看完题解之后,发现解法甚是巧妙,便来分享一则。

原理就是在无向图floyed求最短路的时候,每发现一次新的路径就要更新一次重要点。策略如下:

  1. 如果原路径比现在的路径要长,那么更新路径,并且在cities[i][j]中记录点k。
  2. 如果原路径和现在的路径相等,那么重要点不唯一,那么把cities[i][j]记录为-1。

最终在cities里面搜索不为-1的元素,排序输出即可。

代码

// P1841.cpp
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;
#define ll long long
ll mat[300][300];
int dp[300][300];
bool ans[500];
const ll INF = (ll)(0x7fffffff) / 3;
int n, m;

void floyed()
{
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            if (i != k)
                for (int j = 1; j <= n; j++)
                    if (i != j && j != k)
                        if (mat[i][k] + mat[k][j] < mat[i][j])
                            mat[i][j] = mat[i][k] + mat[k][j], dp[i][j] = k;
                        else if (mat[i][k] + mat[k][j] == mat[i][j])
                            dp[i][j] = -1;
}

void addpath(int src, int dst, ll w)
{
    mat[src][dst] = mat[dst][src] = w;
}

int main()
{
    fill(mat[0], mat[0] + 300 * 300, INF);
    cin >> n >> m;
    for (int i = 0; i < m; i++)
    {
        ll a, b, c;
        cin >> a >> b >> c;
        addpath(a, b, c);
    }
    floyed();
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            if (dp[i][j] != -1)
                ans[dp[i][j]] = true;
    bool flag = true;
    for (int i = 1; i <= n; i++)
        if (ans[i])
            cout << i << " ", flag = false;
    if (flag)
        cout << "No important cities.";
    return 0;
}