CF903F
题解
- '*'用1表示,'.'用0表示。总共4行,从左到右一列一列遍历,使每一列按顺序变成0。
- 每一列的状态为state |= 1<<(4 * j + i)。i表示横坐标,j表示纵坐标。比如第一列全部是1,那么状态为0000 0000 0000 1111。第一行全部是1,状态为0001 0001 0001 0001。
- 具体实现见代码(详细)。
代码
#include <bits/stdc++.h>
using namespace std;
int const N = 1000 + 10;
int const M = (1<<16)+10;
int const inf = 0x7f7f7f7f;
int a[5],n;
char mp[5][N];
int dp[2][M]; //dp[i][j]表示最小花费
vector<int>p[5];
void Init(){
for(int i=1;i<=4;i++){ //枚举矩阵大小
for(int j=0;j+i<=4;j++){ //表示矩阵的横坐标区域为j到j+i
int v = (1<<16) - 1;
for(int r=j;r<j+i;r++)
for(int k=0;k<i;k++) //纵坐标区域
v ^= (1<<(4*k+r)); //要翻转的部分抑或成0
p[i].push_back(v);
}
}
}
void solve(){
int state = 0;
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
if(mp[i][j] == '*') state |= 1<<(4 * j + i);
memset(dp,inf,sizeof(dp));
int up = (1 << 0) + (1 << 1) + (1 << 2) + (1 << 3); //第一列
dp[0][state] = 0;
for(int i=0;i<n;i++){
int id = i & 1; //滚动数组
for(int j=(1<<16)-1;j>=0;j--){
if(dp[id][j] == inf) continue;
if(!(j & up)){ //如果第i列都已经翻转为0
int tmp = j >> 4; //首部去除第i列
for(int k=0;k<4;k++) if(mp[k][i+4] == '*') tmp |= 1<<(4 * 3 + k); //尾部加入第i+4列
dp[id^1][tmp] = dp[id][j]; //保存在下一个滚动数组
}
for(int k=1;k<=4;k++){ //j从(1<<16)-1开始枚举,因为会影响到后面状态,而后面的不会影响的前面的。
for(int l=0;l<p[k].size();l++)
dp[id][j&p[k][l]] = min(dp[id][j&p[k][l]],dp[id][j] + a[k]);
}
}
memset(dp[id],inf,sizeof(dp[id]));
}
printf("%d\n",dp[n&1][0]);
}
int main(){
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();
solve();
return 0;
}