题目链接:http://codeforces.com/contest/225/problem/C
题目大意:
给出一个矩阵,只有两种字符'.'和'#',问最少修改多少个点才能让每一列的字符一致,且字符一致的连续的列的宽度在x和y之间。
解题思路:
先求出每列‘.’和'#'的前缀和,sum[i][0]表示前i列'#' 的前缀和,sum[i][1]表示前i列'.' 的前缀和 ,因为修改要求每列都保持一直,其实我们可以将每列都当成一个点来看,那样我们就相当于是在一维序列上操作了。
dp[i][0]表示最后一列为'.'的最优解,dp[i][1]表示最后一列为'#'的最优解 。
那么我们可以得到状态转移方程:
dp[i+j][0]=min(dp[i+j][0],dp[i][1]+sum[i+j][0]-sum[i][0]),x=<j<=y,0<=i<=m
dp[i+j][1]=min(dp[i+j][1],dp[i][0]+sum[i+j][1]-sum[i][1]),x=<j<=y,0<=i<=m
其实很好理解,dp[i+j][0]表示第i+j列为'.',那么可以由相差为j的第i列为'#'的状态推导过来,同时要将i+1~j的'#'都变为'.'
dp[i+j][1]同理。
代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<vector> 5 #include<string> 6 #include<string.h> 7 #include<cctype> 8 #include<math.h> 9 #include<stdlib.h> 10 #include<stack> 11 #include<queue> 12 #include<set> 13 #include<map> 14 #define lc(a) (a<<1) 15 #define rc(a) (a<<1|1) 16 #define MID(a,b) ((a+b)>>1) 17 #define fin(name) freopen(name,"r",stdin) 18 #define fout(name) freopen(name,"w",stdout) 19 #define clr(arr,val) memset(arr,val,sizeof(arr)) 20 #define _for(i,start,end) for(int i=start;i<=end;i++) 21 #define FAST_IO ios::sync_with_stdio(false);cin.tie(0); 22 using namespace std; 23 typedef long long LL; 24 const int N=3e3+5; 25 const LL INF64=1e18; 26 const int INF=0x3f3f3f3f; 27 const double eps=1e-10; 28 29 int dp[N][2]; //dp[i][0]表示最后一列为'.'的最优解,dp[i][1]表示最后一列为'#'的最优解 30 int sum[N][2]; //sum[i][0]表示前i列'#' 的前缀和,sum[i][1]表示前i列'.' 的前缀和 31 32 int main(){ 33 memset(dp,0x3f,sizeof(dp)); 34 FAST_IO; 35 int n,m,x,y; 36 cin>>n>>m>>x>>y; 37 for(int i=1;i<=n;i++){ 38 for(int j=1;j<=m;j++){ 39 char x; 40 cin>>x; 41 if(x=='#') 42 sum[j][0]++; 43 else 44 sum[j][1]++; 45 } 46 } 47 for(int i=1;i<=m;i++){ 48 sum[i][0]+=sum[i-1][0]; 49 sum[i][1]+=sum[i-1][1]; 50 } 51 52 dp[0][0]=dp[0][1]=0; 53 for(int i=0;i<=m;i++){ 54 for(int j=x;j<=y;j++){ 55 dp[i+j][0]=min(dp[i+j][0],dp[i][1]+sum[i+j][0]-sum[i][0]); 56 dp[i+j][1]=min(dp[i+j][1],dp[i][0]+sum[i+j][1]-sum[i][1]); 57 } 58 } 59 cout<<min(dp[m][0],dp[m][1])<<endl; 60 return 0; 61 }