今天做了下csust的一场比赛,这是A题,先是枚举每一行超时了一次,之后又wa了数次。。。最后还是艰难的ac了。。。同一道题,1A和10A会得到两种完全不同的喜悦感!
题目:小oy是一位文艺青年,他园艺精湛,在自家后院里种满了美丽的线段树。他家后院是一个由n*m个方格组成的矩阵,有些方格上种有线段树。小oy认为:若每个格子的相邻四格(上下左右)的线段树加起来都恰好是偶数棵,那么后院就是美观的。小oy拔掉或新建一棵线段树都需要消耗1点体力,现在他想知道让后院变得美观最少需要消耗多少体力值。
(四角落位置只有两格相邻,边界非角落位置只有三格相邻)
输入:第一行两个正整数n,m。(n<=10, m<=100)接下来n行,每行一个长m的字符串,代表该行的初始状态。(#表示线段树,-表示空地)
输出:最小消耗体力值
Sample Input :
2 4
#-#-
-#--
Sample output:
3
用状态压缩+枚举。状态压缩:用一个数组A表示原矩阵,有线段树的地方置为1,空地置为0。但需要注意的是本题n和m的范围!n<=10 , m<=100,如果枚举每一行的状态,那时间复杂度为O(2^m *n^2) ,肯定超时,所以应该枚举每一列!在输入的时候将行与列转换一下,时间复杂度为O(2^n*m^2),完全可以接受。。。具体方法是:枚举每个第一列,然后依题意确定每个下一列(对于b[r-1][c],已知sum = b[r-2][c]+b[r-1][c-1]+b[r-1][c+1],则当且仅当b[r][c]=sum%2时点(r-1, c)才能满足题意),不过要注意最后一列需要单独判断,因为这个wa了好多次。。。
#include<algorithm> #include<iostream> using namespace std; const int maxn = 111; const int INF = 1e9; int A[maxn][maxn], b[maxn][maxn], n, m; int check(int s) { memset(b, 0, sizeof(b)); for(int c=0; c<n; c++) // 枚举排列 { if(s & (1<<c)) b[0][c] = 1; } for(int r=1; r<m; r++) for(int c=0; c<n; c++) { int sum = 0; //sum为b[r-1][c]的上 左 右 三个之和 if(r > 1) sum += b[r-2][c]; if(c > 0) sum+= b[r-1][c-1]; if(c < n-1) sum+= b[r-1][c+1]; b[r][c] = sum%2; //由sum确定b[r][c] 使b[r-1][c]满足题意 } for(int i=0; i<n; i++) //单独判断最后一列是否满足 { int sum = 0; if(i > 0) sum+=b[m-1][i-1]; if(i < n-1) sum+=b[m-1][i+1]; if(m > 1) sum+=b[m-2][i]; if(sum%2) return INF; } int cnt = 0; for(int r=0; r<m; r++) for(int c=0; c<n; c++) if(A[r][c] != b[r][c]) cnt++;//求体力值 return cnt; } int main() { char ch[maxn]; while(cin >> n >> m) { int sum = 0; for(int i=0; i<n; i++) { cin>>ch; for(int j=0; j<m; j++) { if(ch[j] == '#') { A[j][i] = 1;//行与列转换 sum++; } else { A[j][i] = 0; } } } int ans = INF; for(int s=0; s<(1<<n); s++) // 枚举所有列 { ans = min(ans, check(s)); } if(ans == INF) ans = sum; //若无解 则删除所有‘#’ cout<<ans<<endl; } }