csust组队练习赛第二场A题:Yard(状态压缩)

今天做了下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;
    }
}


你可能感兴趣的:(ACM,状态压缩,yard,CSUST)