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

本文介绍了一个关于线段树的问题,通过状态压缩和枚举的方法,解决如何让一个由线段树组成的矩阵达到美观的标准,即每个格子相邻的四个格子(上下左右)的线段树数量为偶数。文章详细解释了算法实现过程,并给出了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值