[NOIp2015]斗地主

Description

Luogu2688
给定一手牌,问最少几步把这手牌打完。

Solution

DP出散牌,暴力找顺子。

由于顺子可能会出现多余的单牌,所以暴力的枚举如何出顺子才更优。

DP就是一个暴力DP,没有什么有趣的地方,不过不要忘了拆牌。

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
using std::min;

const int N = 25;

int f[N][N][N][N][3];
int mx[N];
int a[N];
int my[N];
int ans = 1000, n;

void init() {
    f[0][0][0][0][0] = 0;
    for (int r = 0; r <= mx[5]; ++r) {
        for (int l = 0; l <= mx[4]; ++l) {
            for (int k = 0; k <= mx[3]; ++k) {
                for (int j = 0; j <= mx[2]; ++j) {
                    for (int i = 0; i <= mx[1]; ++i) {
                        int x = 1000;
                        // 出散牌:
                        if (i) x = min(x, f[i-1][j][k][l][r]);
                        if (j) x = min(x, f[i][j-1][k][l][r]);
                        if (k) x = min(x, f[i][j][k-1][l][r]);
                        if (l) x = min(x, f[i][j][k][l-1][r]);
                        if (r) x = min(x, f[i][j][k][l][r-1]);
                        if (r>1) x = min(x, f[i][j][k][l][r-2]);
                        // 三带/拆三
                        if (k) {
                            if (i) x = min(x, f[i-1][j][k-1][l][r]);
                            if (j) x = min(x, f[i][j-1][k-1][l][r]);
                            if (r) x = min(x, f[i][j][k-1][l][r-1]);
                            if (r>1) x = min(x, f[i][j][k-1][l][r-2]);
                            x = min(x, f[i+1][j+1][k-1][l][r] - 1);
                        }
                        // 四带/拆四
                        if (l) {
                            if (i>1) x = min(x, f[i-2][j][k][l-1][r]);
                            if (i && r) x = min(x, f[i-1][j][k][l-1][r-1]);
                            if (r>1) x = min(x, f[i][j][k][l-1][r-2]);
                            if (j>1) x = min(x, f[i][j-2][k][l-1][r]);
                            if (j && r>1) x = min(x, f[i][j-1][k][l-1][r-2]);
                            if (j) x = min(x, f[i][j-1][k][l-1][r]);
                            if (l>1) x = min(x, f[i][j][k][l-2][r]);
                            x = min(x, f[i+1][j][k+1][l-1][r] - 1);
                        }
                        f[i][j][k][l][r] = min(x + 1, f[i][j][k][l][r]);
                    }
                }
            }
        }
    }
}

void dfs(int x) {
    if (x > ans) return;
    for (int k = 1, flag; k <= 3; ++k) {
        for (int i = 1; i <= 12; ++i) {
            flag = 1;
            int len = 4 / k + 1;
            while (flag && i + len - 1 <= 12) {
                for (int j = 1; j <= len; ++j) {
                    if (a[i+j-1] < k) {
                        flag = 0;
                        break;
                    }
                }
                if (!flag) break;
                for (int j = 1; j <= len; ++j) {
                    a[i+j-1] -= k;
                }
                dfs(x+1);
                for (int j = 1; j <= len; ++j) {
                    a[i+j-1] += k;
                }
                len++;
            }
        }
    }
    memset(my, 0, sizeof my);
    for (int i = 1; i <= 13; ++i) my[a[i]]++;
    my[5] = a[14];
    ans = min(ans, x + f[my[1]][my[2]][my[3]][my[4]][my[5]]);
}

int main() {
    memset(f, 0x3f, sizeof f);
    int t;
    scanf("%d%d
", &t, &n);
    mx[1] = n; mx[2] = n / 2; mx[3] = n / 3; mx[4] = n / 4; mx[5] = 2;
    init();
    while (t--) {
        ans = 1000;
        memset(a, 0, sizeof a);
        for (int i = 1, x, y; i <= n; ++i) {
            scanf("%d%d", &x, &y);
            if (x == 0) a[14]++;
            else if (x <= 2) a[11+x]++; 
            else a[x-2]++;
        }
        dfs(0);
        printf("%d
", ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/wyxwyx/p/noip201513.html