炸弹 树形地图

来源:互联网 发布:大数据交易qtjiaoyi 编辑:程序博客网 时间:2024/06/13 19:19

题目大意:在一个50*50的地图内,有些是空地,另外的是障碍物,空地是连成一棵树。若在某空地放置一个炸弹,那么在同一列和同一行且没有障碍物阻拦的格子都被炸到(类似于泡泡堂。。。)。询问至少需要多少个炸弹才能把所有的格子都炸到。

题目中很关键的一个字眼就所有的空地是一棵树,这促使我们往树形动态规划的方面去想。

构建一棵二叉树,左子树表示在同一行的空地,右子树表示同一列的空地。那么,若把某个节点炸掉,受到影响的将会是它的父亲和祖先,左孩子和左孩子的左孩子,左孩子的左孩子的左孩子,以及右孩子,右孩子的右孩子....。对每个点记录两个状态,一个是该节点被炸以后,有一个向左上的威力把父亲炸掉,以及有一个右上的威力把父亲炸掉。

#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <iostream>#include <algorithm>#include <queue>#include <map>#include <set>using namespace std;#define st first#define nd second#define pi 3.141592653589793238462643383const int dx[4] = {0, 0, 1, -1};const int dy[4] = {1, -1, 0, 0};const int inf = 0x3f3f3f3f;#define Rep(i, n) for (int i = 0, _n = n; i < _n; i ++)#define Repp(i, n) for (int i = 1, _n = n; i <= _n; i ++)#define For(i, a, b) for (int i = a, _b = b; i <= _b; i ++)#define Forr(i, a, b) for (int i = a, _b = b; i >= _b; i --)typedef long long ll;typedef pair<int, int> pii;#define maxn 57#define maxp maxn*maxnint n, m, sx, sy;int dat[maxn][maxn];struct Pointer{    Pointer *lc, *rc;    int lf, rf;        int get() {        return min(lf, rf);    }}head[maxp], *nll = head;void Init() {    scanf("%d%d\n", &n, &m);        nll -> lf = nll -> rf = 0;        int cnt = 0;    char ch;    Repp(i, n) {        Repp(j, m) {            scanf("%c", &ch);            if (ch == '.') {                // 编号                 dat[i][j] = ++ cnt;                head[cnt].lc = head[cnt].rc = nll;                                if (cnt == 1) sx = i, sy = j;            }        }        scanf("\n");    }}void dfs(Pointer *x) {    if (x == nll) return;    dfs(x -> lc);     dfs(x -> rc);        if (x -> rc != nll)         x -> lf = x -> rc -> lf + x -> lc -> get();    else x -> lf = inf;        if (x -> lc != nll)         x -> rf = x -> lc -> rf + x -> rc -> get();    else x -> rf = inf;        // 其实这里可以多记录两个部分和,那么不用每次往下找一次    // 时间复杂度从 O(n ^ 3) 降到 O(n ^ 2)     int tmp = 1;    Pointer *p = x;    while (true) {        p = p -> lc;        if (p == nll) break;        tmp += p -> rc -> get();    }        p = x;    while (true) {        p = p -> rc;        if (p == nll) break;        tmp += p -> lc -> get();    }        x -> lf = min(x -> lf, tmp);    x -> rf = min(x -> rf, tmp);}pii que[maxp];// 构建二叉树 void Graph() {    int quehead = 0, quetail = 0;    que[0].st = sx, que[0].nd = sy;        while (quehead <= quetail) {        pii now = que[quehead ++];                Rep(k, 4) {            pii to;            to.st = now.st + dx[k];            to.nd = now.nd + dy[k];                        if (dat[to.st][to.nd] > 0) {                Pointer *p = &head[dat[now.st][now.nd]];                                // 若已经有(左、右)孩子,则一直找(左、右)孩子的(左、右)孩子。                 if (k < 2) {                    while (p -> lc != nll) p = p -> lc;                    p -> lc = &head[dat[to.st][to.nd]];                }                else {                    while (p -> rc != nll) p = p -> rc;                    p -> rc = &head[dat[to.st][to.nd]];                }                                que[++ quetail] = to;            }        }                dat[now.st][now.nd] = 0;    }}int main() {    freopen("bomb.in", "r", stdin);    freopen("bomb.out", "w", stdout);        Init();        Graph();        dfs(&head[1]);        printf("%d\n", min(head[1].lf, head[1].rf));        return 0;}

原创粉丝点击