[HNOI 2007] bzoj1187 神奇游乐园 [dp]

Description:
求最大权哈密尔顿回路。


Solution:
插头 d p 裸题。
预处理出所有轮廓线的状态,令 d p [ i ] [ j ] [ k ] 表示第 i 行第 j 列轮廓线状态为 k 的最大权值。先预处理出第一行的 d p 值,每次做到末尾时计算下一行第一格。
用三进制表示状态, 04 , 1 , 2 : 1. 2. 3. 4. 5. 6. 7.$左插头和右插头,直接接上。


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n, m, ans = -0x3f3f3f3f;
int dp[105][7][2205], a[105][7], lp[2205][7], rp[2205][7], bin[8];
bool can[2205];
bool check(int S) {
    int s = 0;
    for(int i = 0; i <= n; ++i) {
        if(S / bin[i] % 3 == 1) {
            ++s;
        } else if(S / bin[i] % 3 == 2) {
            --s;
        }
        if(s < 0) {
            return 0;
        }
    }
    return s == 0;
}
int fl(int S, int p) {
    int s = 1;
    for(int i = p - 1; ~i; --i) {
        if(S / bin[i] % 3 == 1) {
            --s;
        } else if(S / bin[i] % 3 == 2) {
            ++s;
        }
        if(s == 0) {
            return i;
        }
    }
}
int fr(int S, int p) {
    int s = 1;
    for(int i = p + 1; i <= n; ++i) {
        if(S / bin[i] % 3 == 2) {
            --s;
        } else if(S / bin[i] % 3 == 1) {
            ++s;
        }
        if(s == 0) {
            return i;
        }
    }
}
void U(int &x, int y) {
    if(x < y) {
        x = y;
    }
}
int calc(int S) {
    int ret = 0, s = 0;
    for(int i = 0; i < n; ++i) {
        if(S / bin[i] % 3 == 1) {
            ++s;
            ret += a[1][i + 1];
        } else if(S / bin[i] % 3 == 2) {
            --s;
            ret += a[1][i + 1];
        } else if(s) {
            ret += a[1][i + 1];
        }
        if(s > 1) {
            return -0x3f3f3f3f;
        }
    }
    return ret;
}
int main() {
    scanf("%d%d", &m, &n);
    bin[0] = 1;
    for(int i = 1; i <= n + 1; ++i) {
        bin[i] = bin[i - 1] * 3;
    }
    for(int i = 1; i <= m; ++i) {
        for(int j = 1; j <= n; ++j) {
            scanf("%d", &a[i][j]);
        }
    }
    memset(dp, -0x3f3f, sizeof(dp));
    for(int i = 0; i < bin[n + 1]; ++i) {
        if(check(i)) {
            can[i] = 1;
            for(int j = 0; j <= n; ++j) {
                int bit = i / bin[j] % 3;
                if(bit == 1) {
                    rp[i][j] = fr(i, j);
                } if(bit == 2) {
                    lp[i][j] = fl(i, j);
                }
            }
        }
        if(!(i / bin[n])) {
            dp[1][n][i] = calc(i);
        }
    }
    for(int i = 2; i <= m; ++i) {
        for(int S = 0; S < bin[n + 1]; ++S) {
            if(can[S] && !(S / bin[n])) {
                int S0 = (S - S % 3) * 3;
                if(S / bin[0] % 3 == 1) {
                    U(dp[i][1][S0 + 1], dp[i - 1][n][S] + a[i][1]);
                    U(dp[i][1][S0 + 3], dp[i - 1][n][S] + a[i][1]);
                } else if(S / bin[0] % 3 == 0) {
                    U(dp[i][1][S0 + 7], dp[i - 1][n][S] + a[i][1]);
                    U(dp[i][1][S0], dp[i - 1][n][S]);
                }
            }
        }  
        for(int j = 2; j <= n; ++j) {
            for(int S = 0; S < bin[n + 1]; ++S) { 
                if(can[S]) {
                    int u = S / bin[j - 1] % 3, v = S / bin[j] % 3, S0 = S - bin[j - 1] * u - bin[j] * v, tmp = dp[i][j - 1][S];
                    if(!u && !v) {
                        U(dp[i][j][S0 + bin[j - 1] + 2 * bin[j]], tmp + a[i][j]);
                        U(dp[i][j][S0], tmp);
                    }
                    if((!u && v == 1) || (u == 1 && !v)) {
                        U(dp[i][j][S0 + bin[j - 1]], tmp + a[i][j]);
                        U(dp[i][j][S0 + bin[j]], tmp + a[i][j]);
                    }
                    if((!u && v == 2) || (u == 2 && !v)) {
                        U(dp[i][j][S0 + bin[j - 1] * 2], tmp + a[i][j]);
                        U(dp[i][j][S0 + bin[j] * 2], tmp + a[i][j]);
                    }
                    if(u == 1 && v == 1) {
                        U(dp[i][j][S0 - bin[rp[S][j]]], tmp + a[i][j]);
                    }
                    if(u == 2 && v == 2) {
                        U(dp[i][j][S0 + bin[lp[S][j - 1]]], tmp + a[i][j]);
                    }
                    if(u == 1 && v == 2) {
                        if(!S0) {
                            U(ans, tmp + a[i][j]);
                        }
                    }
                    if(u == 2 && v == 1) {
                        U(dp[i][j][S0], tmp + a[i][j]);
                    }
                }
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/pocket_lengend/article/details/80054833