Codeforces-903F Clear The Matrix(状压DP)

F. Clear The Matrix
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

You are given a matrix f with 4 rows and n columns. Each element of the matrix is either an asterisk (*) or a dot (.).

You may perform the following operation arbitrary number of times: choose a square submatrix of f with size k × k (where 1 ≤ k ≤ 4) and replace each element of the chosen submatrix with a dot. Choosing a submatrix of size k × k costs ak coins.

What is the minimum number of coins you have to pay to replace all asterisks with dots?

Input

The first line contains one integer n (4 ≤ n ≤ 1000) — the number of columns in f.

The second line contains 4 integers a1a2a3a4 (1 ≤ ai ≤ 1000) — the cost to replace the square submatrix of size 1 × 12 × 23 × 3or 4 × 4, respectively.

Then four lines follow, each containing n characters and denoting a row of matrix f. Each character is either a dot or an asterisk.

Output

Print one integer — the minimum number of coins to replace all asterisks with dots.

Examples
input
4
1 10 8 20
***.
***.
***.
...*
output
9
input
7
2 1 8 2
.***...
.***..*
.***...
....*..
output
3
input
4
10 10 1 10
***.
*..*
*..*
.***
output
2
Note

In the first example you can spend 8 coins to replace the submatrix 3 × 3 in the top-left corner, and 1 coin to replace the 1 × 1 submatrix in the bottom-right corner.

In the second example the best option is to replace the 4 × 4 submatrix containing columns 2 – 5, and the 2 × 2 submatrix consisting of rows 2 – 3 and columns 6 – 7.

In the third example you can select submatrix 3 × 3 in the top-left corner and then submatrix 3 × 3 consisting of rows 2 – 4 and columns2 – 4.


题解:状压DP+完全背包

因为方块最大只能取4*4,因此当前为第i列时,只与i-3~i列的状态有关,状态可压缩为1<<16种,
设'*'为1,'.'为0,首先预处理出把大小为i的方块的顶部放在第0~3行时的状态mark(底部为第j+k-1行)

当前为第i列时,如果不断放方块,则状态S的大小是不增的(这一点是这个问题的关键,因为会不断把'*'变成'.'),
这样就类似于完全背包的状态转移dp[i][j]=min(dp[i][j],dp[i][j-w[i]]+v[i])
由此得到状态转移方程为dp[i][S&mark[j][k]]=min(dp[i][S&mark[j][k]],dp[i][S]+a[j])
其中mark[j][k]表示把大小为j的方块的顶部放在第k行时的状态

当第i列全部变成'.'时,则可以转移到第i+1行,此时S&15==0


一开始一直没想到怎么利用这个单调性,总是不知道怎么放方块,还是太菜了(气哭

#include
#define FIN freopen("in.txt","r",stdin);
using namespace std;
typedef long long LL;
typedef pair PII;
const int INF = 0x3f3f3f3f;
const int MX = 1e3 + 5;
const int MM = (1 << 16) - 1;

int n, a[5];
int dp[MX][MM + 5], r[MX];
char mp[4][MX];
vectormark[5];

int get_sta(int l, int x) {
    int ret = 0;
    for(int i = 0; i < x; i++) {
        for(int j = 0; j < x; j++) {
            ret |= 1 << (4 * j + l + i);
        }
    }
    return ret;
}

int get_row(int i) {
    int ret = 0;
    for(int j = 0; j < 4; j++) if(mp[j][i] == '*') ret |= 1 << j;
    return ret;
}

void init() {
    for(int i = 1; i <= 4; i++) {
        for(int j = 0; j + i <= 4; j++) {
            mark[i].push_back(MM ^ get_sta(j, i));
        }
    }
    for(int i = 0; i < n; i++) r[i] = get_row(i);
}

int main() {
    //FIN;
    scanf("%d", &n);
    for(int i = 1; i <= 4; i++) scanf("%d", &a[i]);
    for(int i = 0; i < 4; i++) scanf("%s", mp[i]);
    init();
    memset(dp, 0x3f, sizeof(dp));
    int S = 0;
    for(int i = 0; i < min(4, n); i++) S |= r[i] << (i * 4);
    dp[0][S] = 0;
    for(int i = 0; i < n; i++) {
        for(int S = MM; S >= 0; S--) {
            if(dp[i][S] == INF) continue;
            if((S & 15) == 0) {
                int SS = S >> 4 | (r[i + 4] << 12);
                dp[i + 1][SS] = min(dp[i + 1][SS], dp[i][S]);
            }
            for(int j = 1; j <= 4 && i + j <= n; j++) {
                for(auto SS : mark[j]) {
                    dp[i][S & SS] = min(dp[i][S & SS], dp[i][S] + a[j]);
                }
            }
        }
    }
    printf("%d\n", dp[n][0]);
    return 0;
}


你可能感兴趣的:(DP,状压dp)