P3671 [USACO17OPEN] Where's Bessie? S 题解

Posted on 2024-03-02 17:03  _XOFqwq  阅读(24)  评论(0编辑  收藏  举报

我们先枚举所有子矩阵,验证其在不考虑重叠的情况下是否为 PCL 矩阵(dfs 求一遍联通块即可)。

然后将所有满足条件的矩阵存下来,最后朴素判断每个矩阵是否被其他矩阵包括,若没有矩阵包括它,则其对于答案的贡献为 \(1\),累加所有贡献即为最终结果。

时间复杂度是 \(O(n^6)\) 的。

思路很简单,讲一下坑点:

  • dfs 求联通块时,注意坐标范围不是 \(1 \sim n\) 而是当前子矩阵的左上角坐标 \(\sim\) 右下角坐标。

  • 枚举子矩阵时,左下角坐标可以等于右下角坐标,所以注意循环变量的初始值设定。

代码(略有压行):

#include<bits/stdc++.h>
#define int long long
#define ft first
#define sc second
using namespace std;
int n,ans,tot,dx[]={0,0,-1,1},dy[]={-1,1,0,0};
char mp[31][31];
bool fl[31][31]; //标记联通块
pair<pair<int,int>,pair<int,int> > mat[160031]; //存放合法矩阵
void dfs(int x,int y,int n1,int n2,int m1,int m2,char c){ //dfs 求联通块
    fl[x][y]=1;
    for(int i=0;i<4;i++){
        int xx=x+dx[i],yy=y+dy[i];
        if(xx>=n1&&xx<=n2&&yy>=m1&&yy<=m2&&!fl[xx][yy]&&mp[xx][yy]==c) dfs(xx,yy,n1,n2,m1,m2,c);
    }
}
bool check(int x,int y,int xx,int yy){ //校验矩阵
    char c1=' ',c2=' '; int cnt1=0,cnt2=0; //分别表示:两种不同类型的字符、两种字符的联通块数量
    for(int i=x;i<=xx;i++){ for(int j=y;j<=yy;j++){ if(c1==' '){ c1=mp[i][j]; break; } } } //找出第一种字符
    for(int i=x;i<=xx;i++){ for(int j=y;j<=yy;j++){ if(c2==' '&&mp[i][j]!=c1){ c2=mp[i][j]; break; } } } //找出第二种字符
    for(int i=x;i<=xx;i++) for(int j=y;j<=yy;j++) if(mp[i][j]!=c1&&mp[i][j]!=c2) return 0; //若字符种类超过两种一定不合法
    memset(fl,0,sizeof(fl));
    for(int i=x;i<=xx;i++) for(int j=y;j<=yy;j++)
            if(mp[i][j]==c1&&!fl[i][j]) cnt1++,dfs(i,j,x,xx,y,yy,c1); //求第一种字符的联通块数量
    for(int i=x;i<=xx;i++) for(int j=y;j<=yy;j++)
            if(mp[i][j]==c2&&!fl[i][j]) cnt2++,dfs(i,j,x,xx,y,yy,c2); //求第二种字符的联通块数量
    if((cnt1==1&&cnt2>1)||(cnt1>1&&cnt2==1)) return 1; return 0; //若一种字符的联通块数量>1且另一种=1则合法,否则不合法
}

signed main(){
	ios::sync_with_stdio(0);
	cin>>n;
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>mp[i][j];
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)
	       for(int ii=i;ii<=n;ii++) for(int jj=j;jj<=n;jj++)
	               if(check(i,j,ii,jj)) mat[++tot].ft.ft=i,mat[tot].ft.sc=j,mat[tot].sc.ft=ii,mat[tot].sc.sc=jj; //存放矩阵
	for(int i=1;i<=tot;i++){
	    bool f=1;
	    for(int j=1;j<=tot;j++) if(i!=j&&mat[i].ft.ft>=mat[j].ft.ft&&mat[i].ft.sc>=mat[j].ft.sc&&mat[i].sc.ft<=mat[j].sc.ft&&mat[i].sc.sc<=mat[j].sc.sc){ f=0; break; } //遍历矩阵判断是否覆盖
	    if(f) ans++; //累加贡献
	}
	cout<<ans;
    return 0;
}