[SMOJ1894]战争

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013686535/article/details/77370332

解决这道题目,关键是分析出目标是什么,手段是什么。

不妨这样考虑,对于一个基地或发电站,可以选择任意炮台对其进行打击,且题目没有对所选炮台进行任何次数或次序等任何限定。显然,如果要击毁它,应该直接选择消耗能量最小的炮台。这样,每个基地和发电站都可以确定唯一的“被击毁所消耗能量”。因此,炮台就可以不再考虑了。

现在,最终目的是摧毁所有基地。要么选择直接击毁这个基地,要么将其可以依赖的所有发电站都摧毁,最终使得不存在任何一个基地还能依赖发电站存活。
可以发现这其实就是一个最小割的模型。将基地作为 X 集合,发电站作为 Y 集合,每个基地向其可依赖的发电站连一条容量为 的边,就得到了二分图。
再从源点向 X 集合中的点连边,容量为击毁对应炮台所需的最小能量;从 Y 集合中的点向汇点连边,容量为击毁对应发电站所需的最小能量。要么割左边的边(打基地),要么割右边的边(打发电站),使得 st 不连通(也就是所说的“不存在任何一个基地还能依赖发电站存活”)。

显然所求即为网络的最小割,于是可以转化为最大流求解。
总结:这样的问题,直接分析最大流是想不出来的,应该结合题目的具体情况,从条件入手,发掘一些性质(例如炮台在网络中根本不需要考虑),建立最小割模型之后,才能转化为最大流。感觉自己赛场上有一点做得并不够好,没有体会题目的目标,思考可行的手段,花了比较长的时间才意识到最小割。

参考代码:

//1894.cpp
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>

using namespace std;

const int MAXN = 50 + 10;
const int MAXM = 50 + 10;
const int MAXK = 50 + 10;
const int INF = 0x3f3f3f3f;

typedef pair <int, int> pii;

struct Edge {
    Edge *next;
    int cap;
    int dest;
} edges[(MAXM + MAXK + MAXM * MAXK) << 1], *current, *first_edge[MAXM + MAXK];

int N, M, K, L, s, t;
pii pt[MAXN], jd[MAXM], fdz[MAXK];
bool vis[MAXM + MAXK];

inline int sqr(int x) { return x * x; }

inline int dist1(pii a, pii b) { return sqr(a.first - b.first) + sqr(a.second - b.second); } //欧几里德距离的平方

inline double dist2(pii a, pii b) { return sqrt(dist1(a, b)); };

Edge *counterpart(Edge *x) { return edges + ((x - edges) ^ 1); }

void insert(int u, int v, int c) {
    current -> next = first_edge[u];
    current -> cap = c;
    current -> dest = v;
    first_edge[u] = current ++;
}

int dfs(int u, int f) {
    if (u == t) return f;
    if (vis[u]) return 0; else vis[u] = true;
    for (Edge *p = first_edge[u]; p; p = p -> next)
        if (p -> cap)
            if (int res = dfs(p -> dest, min(f, p -> cap))) {
                p -> cap -= res;
                counterpart(p) -> cap += res;
                return res;
            }
    return 0;
}

int main(void) {
    freopen("1894.in", "r", stdin);
    freopen("1894.out", "w", stdout);
    scanf("%d%d%d%d", &N, &M, &K, &L);
    for (int i = 0; i < N; i++) scanf("%d", &pt[i].first); //不得不吐槽这个恶心的输入格式
    for (int i = 0; i < N; i++) scanf("%d", &pt[i].second);
    for (int i = 0; i < M; i++) scanf("%d", &jd[i].first);
    for (int i = 0; i < M; i++) scanf("%d", &jd[i].second);
    for (int i = 0; i < K; i++) scanf("%d", &fdz[i].first);
    for (int i = 0; i < K; i++) scanf("%d", &fdz[i].second);

    s = 0; t = M + K + 1; current = edges;
    fill(first_edge, first_edge + t + 1, (Edge*)0);
    for (int i = 0; i < M; i++) {
        int d = dist1(pt[0], jd[i]);
        for (int j = 1; j < N; j++) d = min(d, dist1(pt[j], jd[i])); //取击毁当前基地所耗能量最小的基地
        insert(s, i + 1, d); insert(i + 1, s, 0);
    }
    for (int i = 0; i < M; i++) //建立二分图
        for (int j = 0; j < K; j++) if (dist2(jd[i], fdz[j]) <= L) insert(i + 1, M + j + 1, INF), insert(M + j + 1, i + 1, 0);
    for (int i = 0; i < K; i++) {
        int d = dist1(pt[0], fdz[i]);
        for (int j = 1; j < N; j++) d = min(d, dist1(pt[j], fdz[i]));
        insert(M + i + 1, t, d); insert(t, M + i + 1, 0);
    }
    int ans = 0;
    while (true) {
        memset(vis, false, sizeof vis);
        if (int res = dfs(s, INF)) ans += res; else break;
    }
    printf("%d\n", ans);
    return 0;
}


阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页