某岛

… : "…アッカリ~ン . .. . " .. .
April 8, 2012

SRM 539

按照 250 -> 1000 -> 550… 的顺序开的题。。
结果 1000 没撸出来 250 又挂了果断爆了 0 。。(但是居然 Unrate 了我会乱说 .. ??。。)
。。。言归正传。。

Brief description:

550:

1000:
给定一颗树状迷宫,定义一次染色操作为:每次覆盖一格点,同时覆盖其上下左右四个方向所有可以延伸到的其它格点。
问至少进行多少次操作可完成染色。
(.. . N ≤ 50 .. )

Analysis:

… 现场生算法:对每个转角位置建点,若两个转角位置通过路径相连则连边。。。
。。于是转化成一个变形的支配集问题。。(点集支配边集。。)。。

(于是这个方法还有待研究。。。)

第二种方法是对每条横竖边分离,根据转角位置连边。。转换成二分图边覆盖问题。。
。。但是有一些奇怪的树状数据这种方法会错。。。

。。。看来这种建模方法好像会丢拓扑信息。。(。。我试图贪心修正。。但看起来这种方法已经没救了。。)
。。于是对每个孤立的格点分别建点。。进行裸的动态规划。。

然后类比树的支配集问题,将状态分以下三类。。。

const int None = 0, Cover = 1, Extend = 2;   
// 还未被覆盖... 被覆盖但不可延伸 ... 被覆盖且可延伸...

之后枚举插头形状分情况讨论状态转移 或 做树上背包即可。。状态转移请看下图。。问号地区请自行脑补。。

-/0/1
? 0 ? // None

 -/1
? 1 ? // Cover

  2
? 2 ? // Extend

  x
x 2 x // Guard

额。。下面以第三副图举例,要求正前方区域必须是 Extend,
同时左右两边要能安全覆盖,要不然是有一个 Extend,要不然是两面都是 Cover 。。。
具体书写的时候用 Delta 存一个差值。。和树的支配集的方法一样。。

另外值得注意的是,对于根结点需要默认一个正方向。

const int dx[] = {1, -1, 0, 0};
const int dy[] = {0, 0, 1, -1};
const int N = 50, NN = N * N;

bool Grid[N][N]; int dp[3][N][N];
int n, m;

inline int _R(int d){
    return d ^ 1;
}

inline int inGrid(int x, int y){
    return x >= 0 && x < n && y >= 0 && y < m;
}

inline bool legal(int x, int y){
    return inGrid(x, y) && Grid[x][y];
}

#define u0 dp[0][x][y]
#define u1 dp[1][x][y]
#define u2 dp[2][x][y]
#define v0 dp[0][xx][yy]
#define v1 dp[1][xx][yy]
#define v2 dp[2][xx][yy]
#define v12 min(v1, v2)
#define v012 min(v0, v12)

void dfs(int x, int y, int dir){

    int c_ = 0, c0 = INF, c1 = INF, c2 = INF;
    int s1 = 0, s12 = 0, s012 = 0, delta = INF, guard = 1;

    bool leaf = true; REP(i, 4){
        if (_R(i) == dir) continue;
        int xx = x + dx[i], yy = y + dy[i];
        if (legal(xx, yy)){
            dfs(xx, yy, i); if (i == dir || dir == -1 && i <= 1) c_ = INF, c0 = v0, c1 = v1, c2 = v2;
            else s1 += v1, s12 += v12, s012 += v012, checkMin(delta, v2 - v012);
            guard += v012, leaf = false;
        }
    }

    if (leaf) u0 = 0, u1 = INF, u2 = 1;
    else {
        s012 += delta, u0 = min(c_, c0, c1) + min(s1, s012), u1 = min(c_, c1) + s012;
        u2 = min(c2 + min(s1, s012), guard);
    }
}

class FoxBomb {
public:
	int getMinimumCost(vector <string> grid){

        n = SZ(grid), m = SZ(grid[0]); int x0 = -1, y0;
	    REP_2(i, j, n, m) Grid[i][j] = grid[i][j] == '.';

        REP_2(x, y, n, m) if (Grid[x][y]) REP(d, 4){
            int xx = x + dx[d], yy = y + dy[d];
            if (legal(xx, yy)) x0 = xx, y0 = yy;
        }

        dfs(x0, y0, -1);
        return min(dp[1][x0][y0], dp[2][x0][y0]);
	}
};