NOJ2214题目解题笔记

本文记录了一道NOJ2214 ACM竞赛题目,题目涉及电视像素矩阵和动态规划算法。给定一个n*m的像素矩阵,其中'.'代表空白,'*'代表有色像素,当有色像素掉落时会造成模糊效果。任务是求解在模糊情况下,MiaoMiao能看到的最少有色像素数。通过动态规划方法,分析上一行对当前行像素选择的影响,并利用状态转移方程进行计算。代码实现和样例输出一并给出。

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

原题链接:http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=2214

比赛描述

MiaoMiao和Bird喜欢一起看电视,他们都有一些近视。

有次他们看电视的时候,Bird的眼镜一不小心掉到地上,这下Bird看到电视画面都变模糊了。确切来说,我们把电视上的像素分成两种,用.表示空白区域,用*表示有颜色区域。

电视的画面为n*m的像素矩阵,对于每一个MiaoMiao看到的有色像素*,Bird都会看成

.*.

***

.*.

模糊的方块会相互重叠,可能会使原本空白区域看起来像有颜色的区域。

现在给出Bird看到的电视画面,请你求出MiaoMiao看到的电视画面中有颜色像素的最小值。




输入

第一行两个正整数n(1 ≤ n ≤ 8)和m(1 ≤ m ≤ 8)

接下来有n行,每一行有m个字符('.'或'*')。


输出

输出一个整数,表示MiaoMiao可能看到的有颜色像素数量的最小值,如果Bird看到的情况不存在,则输出-1。

样例输入

3 3
.*.
***
.*.

样例输出

1

题目来源

MoliH


动态规划。

我没有找到什么高效的解法,复杂度n*4^m(如果m>n,可以把整个电视屏幕转置,变成m*4^n),但是所幸n,m都很小。

首先对于每一行每个点是否有像素点的选择会受到上一行的影响。

int flagarray[]记录每个点上一行对应点情况。

flagarray[i]=0 上一行i处为空  

flagarray[i]=1上一行i处看到是像素点并且周围没有真正的像素点 

flagarray[i]=2上一行i处看到是像素点并且周围有真正的像素点  

flagarray[i]=3上一行i处为真正像素点

当flagarray[i]=1,那么这一行的i处必须为真正的像素点,否则,上一行i处看到的就是空白了。

当flagarray[i]=0,那么这一行的i处必须不为真正的像素点,否则,上一行i处看到的就是像素点了。

当flagarray[i]=2,这一行i出可以有真正的像素点。

当flagarray[i]=3,这一行i处以及周围可以不必在放置真正像素点。

当第i行的flagarray[]已经确定的情况下,那么从第i行到最后一行的所有结果已经是一个确定值了。

不妨定义dp[i][flag],记录在第i行flagarray下,从i到n-1行的真正像素点数量。(flag=flagarray[0]+flagarray[1]*4+flagarray[2]*4^2+...)

每一行flag的所有可能性最多为4^m,n行一共n*4^m。

以下为代码

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <queue>
#include <math.h>
using namespace std;
int p[8][8];
int dp[8][65536];
int n;
int m;
int m2;
int ercifang[]= {1,2,4,8,16,32,64,128};
//#define debug 0
int get1s(int value)
{
    int res=0;
    while(value)
    {
        if(value&1)
            res++;
        value>>=1;
    }
    return res;
}
//flagarray[i] 0 上一行i处为空  1上一行i处为未覆盖点 2上一行i处为覆盖点 3上一行i处为像素中心点

int testvalueflag(int line,int value,int flagarray[])
{
    for(int i=0; i<m; i++)
    {
        if(flagarray[i]==0&&(value&ercifang[i]))
            return 0;
        if(flagarray[i]==1&&((value&ercifang[i])==0))
            return 0;
        if(p[line][i]==0)
        {
            if(i>=1&&(value&ercifang[i-1]))
                return 0;
            if(i<m-1&&(value&ercifang[i+1]))
                return 0;
            if(value&ercifang[i])
                return 0;
            if(flagarray[i]==3)
                return 0;
        }
        else  if(line==n-1)
        {
            if(i>=1&&(value&ercifang[i-1]))
                continue;
            if(i<m-1&&(value&ercifang[i+1]))
                continue;
            if(value&ercifang[i])
                continue;
            if(flagarray[i]==3)
                continue;
            return 0;
        }
    }
    return 1;
}

int run(int line,int flag)
{
    if(dp[line][flag]>=0)
        return dp[line][flag];
    int res=1000000;
#ifdef debug
    int lastvaluearray[8];
#endif
    int flagarray[8];
    int flagcopy=flag;
    memset(flagarray,0,8*sizeof(int));
    for(int i=0; i<8; i++)
    {
        flagarray[i]=flagcopy&3;
        flagcopy>>=2;
    }
    for(int value=0; value<m2; value++)
    {
        if(testvalueflag(line,value,flagarray))
        {
            int rest;
            if(line==n-1)
                rest=0;
            else
            {
                int nextflagarraay[8];
                memset(nextflagarraay,0,8*sizeof(int));
                //flag 0 上一行此处为空  1上一行此处为未覆盖点 2上一行此处为覆盖点 3上一行此处为像素中心点
                for(int i=0; i<m; i++)
                {
                    if(p[line][i]==0)
                        nextflagarraay[i]=0;
                    else
                    {
                        if(value&ercifang[i])
                            nextflagarraay[i]=3;
                        else if(flagarray[i]==3)
                            nextflagarraay[i]=2;
                        else if(i>=1&&(value&ercifang[i-1]))
                            nextflagarraay[i]=2;
                        else if(i<m-1&&(value&ercifang[i+1]))
                            nextflagarraay[i]=2;
                        else nextflagarraay[i]=1;
                    }
                }
                int nextflag=0;
                for(int i=m-1; i>=0; i--)
                {
                    nextflag<<=2;
                    nextflag|=nextflagarraay[i];
                }
                rest=run(line+1,nextflag);
            }
            int thisline=get1s(value);
            if(res>rest+thisline)
            {
#ifdef debug
                int valuecopy=value;

                memset(lastvaluearray,0,8*sizeof(int));

                for(int i=0; i<8; i++)
                {
                    lastvaluearray[i]=valuecopy%2;
                    valuecopy/=2;
                }
#endif
                res=rest+thisline;
            }
        }
    }
    dp[line][flag]=res;
#ifdef debug
    cout<<"line"<<line<<"\nflag:";
    for(int i=0; i<8; i++)
        cout<<flagarray[i]<<' ';
    cout<<endl;
    cout<<"value";
    for(int i=0; i<8; i++)
        cout<<lastvaluearray[i]<<' ';
    cout<<endl<<"res"<<res<<endl;
#endif
    return res;
}
int main()
{
#ifdef debug

    freopen("in.txt","r",stdin);
#endif


    cin>>n>>m;

    memset(dp,0xff,8*65536*sizeof(int));
    for(int i=0; i<n; i++)
    {
        char str[10];
        scanf("%s",str);
        for(int j=0; j<m; j++)
        {
            if(str[j]=='.')
                p[i][j]=0;
            else
                p[i][j]=1;
        }
    }
    if(n<m)
    {
        for(int i=0; i<8; i++)
            for(int j=0; j<i; j++)
                swap(p[i][j],p[j][i]);
        swap(n,m);
    }
    m2=pow(2,m);
    //43686=2+2*4+2*4^2+...+2*4^7

    int ans=run(0,43690);
    if(ans>64)
        printf("-1");
    else
        printf("%d",ans);
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值