zoukankan      html  css  js  c++  java
  • 「NOI2009」植物大战僵尸

    「NOI2009」植物大战僵尸

    传送门
    这是一道经典的最大权闭合子图问题,可以用最小割解决(不会的可以先自学一下)
    具体来说,对于这道题,我们对于两个位置的植物 (i)(j) ,如果 (j) 可以保护 (i) ,也就是 (i)(j) 的攻击范围内(特别的,我们认为一个植物也会被它右边的第一个植物保护),我们就连边 (i o j),因为攻击一个植物的前提就是攻击掉所有保护它的植物,我们这样连边恰好可以满足闭合子图的性质。
    但是我们还会碰到一个问题,那就是互相保护的情况,在图上也就是环的情况,我们发现一个环是不可能被攻击的,所以说我们只把没有位于环上的点用来建图,具体可以用反图+拓扑排序实现。
    这样就解决了。
    参考代码:

    #include <cstring>
    #include <cstdio>
    #define rg register
    #define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
    template < class T > inline T min(T a, T b) { return a < b ? a : b; }
    template < class T > inline void read(T& s) {
        s = 0; int f = 0; char c = getchar();
        while ('0' > c || c > '9') f |= c == '-', c = getchar();
        while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
        s = f ? -s : s;
    }
    
    const int _ = 605, __ = 400005, INF = 2147483647;
    
    int tot = 1, head[_]; struct Edge { int ver, cap, nxt; } edge[__ << 1];
    inline void Add_edge(int u, int v, int d) { edge[++tot] = (Edge) { v, d, head[u] }, head[u] = tot; }
    inline void link(int u, int v, int d) { Add_edge(u, v, d), Add_edge(v, u, 0); }
    
    int n, m, a[_], able[_], dgr[_];
    int s, t, dep[_], cur[_];
    int num; struct node { int u, v; } p[__];
    int hd, tl, Q[_];
    
    inline int id(int i, int j) { return (i - 1) * m + j; }
    
    inline int bfs() {
        memset(dep, 0, sizeof (int) * (t - s + 1));
        hd = tl = 0;
        Q[++tl] = s, dep[s] = 1;
        while (hd < tl) {
    	    int u = Q[++hd];
    	    for (rg int i = head[u]; i; i = edge[i].nxt) {
        	    int v = edge[i].ver;
    	        if (dep[v] == 0 && edge[i].cap > 0)
    		    dep[v] = dep[u] + 1, Q[++tl] = v;
    	    }
        }
        return dep[t] > 0;
    }
    
    inline int dfs(int u, int flow) {
        if (u == t) return flow;
        for (rg int &i = cur[u]; i; i = edge[i].nxt) {
    	    int v = edge[i].ver;
    	    if (dep[v] == dep[u] + 1 && edge[i].cap > 0) {
        	    int res = dfs(v, min(flow, edge[i].cap));
    	        if (res) { edge[i].cap -= res, edge[i ^ 1].cap += res; return res; }
    	    }
        }
        return 0;
    }
    
    inline int Dinic() {
        int res = 0;
        while (bfs()) {
    	    for (rg int i = s; i <= t; ++i) cur[i] = head[i];
    	    while (int d = dfs(s, INF)) res += d;
        }
        return res;
    }
    
    inline void toposort() {
        tot = 0, memset(head, 0, sizeof head);
        for (rg int i = 1; i <= num; ++i) Add_edge(p[i].u, p[i].v, 0), ++dgr[p[i].v];
        hd = tl = 0;
        for (rg int i = 1; i <= n * m; ++i) if (dgr[i] == 0) Q[++tl] = i;
        while (hd < tl) {
    	    int u = Q[++hd]; able[u] = 1;
    	    for (rg int i = head[u]; i; i = edge[i].nxt) if (!--dgr[edge[i].ver]) Q[++tl] = edge[i].ver;
        }
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
        file("cpp");
    #endif
        read(n), read(m), s = 0, t = n * m + 1;
        for (rg int w, x, y, i = 1; i <= n; ++i)
    	    for (rg int j = 1; j <= m; ++j) {
        	    read(a[id(i, j)]), read(w);
    	        while (w--) read(x), read(y), ++x, ++y, p[++num] = (node) { id(i, j), id(x, y) };
    	    }
        for (rg int i = 1; i <= n; ++i)
    	    for (rg int j = 1; j < m; ++j) p[++num] = (node) { id(i, j + 1), id(i, j) };
        toposort();
        tot = 1, memset(head, 0, sizeof head);
        for (rg int i = 1; i <= num; ++i) {
    	    int u = p[i].u, v = p[i].v;
    	    if (!able[u] || !able[v]) continue ;
    	    link(v, u, INF);
        }
        int sum = 0;
        for (rg int i = 1; i <= n * m; ++i) {
    	    if (!able[i]) continue ;
    	    if (a[i] > 0) link(s, i, a[i]), sum += a[i];
    	    if (a[i] < 0) link(i, t, -a[i]);
        }
        printf("%d
    ", sum - Dinic());
        return 0;
    }
    
  • 相关阅读:
    Android学习笔记14:Tween Animation动画的实现
    Android学习笔记17:单项选择RadioButton和多项选择CheckBox的使用
    北国的雪
    Android学习笔记11:图像的平移、旋转及缩放
    三极管基本放大电路解析
    51单片机中data,idata,xdata,pdata的区别
    充电开关制作
    慢慢学Linux驱动开发,第五篇,初探设备模型概念
    慢慢学Linxu驱动开发,第二篇:启程,模块机制,Hello World
    H桥电机驱动原理与应用
  • 原文地址:https://www.cnblogs.com/zsbzsb/p/12249979.html
Copyright © 2011-2022 走看看