题意:给你一个矩阵,有些点是黑的,让你横切h刀,纵切v刀,问你是否能让切出的所有子矩阵的黑点数量相等。
设黑点总数为sum,sum必须能整除(h+1),进而sum/(h+1)必须能整除(v+1)。
先考虑横行,贪心地扫过去,如果到了某一行,当前统计的黑点数恰好为sum/(h+1),就在这里切一刀,接着统计。否则,如果>sum/(h+1),则无解。在这个过程中,每一行被切到哪一横组里就确定了。
然后纵切,过程跟横切类似,只不过统计的不是一个变量,而是一个大小为(h+1)的数组,如果到了某一列,当前统计的数组的每个分量的黑点数恰好为sum/(h+1)/(v+1),就在这里切一刀,接着统计。否则,如果数组的某个分量>sum/(h+1),则无解。
#include<cstdio>
#include<cstring>
using namespace std;
int T,n,m,h,v;
char a[105][105];
int hq[105];
int cnts[105],t;
bool check1(){for(int i=1;i<=h+1;++i){if(cnts[i]!=t){return 0;}}return 1;
}
bool check2(){for(int i=1;i<=h+1;++i){if(cnts[i]>t){return 0;}}return 1;
}
int main(){//freopen("a.in","r",stdin);scanf("%d",&T);for(int zu=1;zu<=T;++zu){printf("Case #%d: ",zu);scanf("%d%d%d%d",&n,&m,&h,&v);for(int i=1;i<=n;++i){scanf("%s",a[i]+1);}int sum=0;for(int i=1;i<=n;++i){for(int j=1;j<=m;++j){sum+=(a[i][j]=='@');}}if(sum%(h+1)!=0){puts("IMPOSSIBLE");continue;}t=sum/(h+1);int cnt=0;bool flag=1;int last=0,num=0;for(int i=1;i<=n;++i){for(int j=1;j<=m;++j){cnt+=(a[i][j]=='@');}if(cnt==t){++num;for(int k=last+1;k<=i;++k){hq[k]=num;}last=i;cnt=0;}else if(cnt>t){flag=0;break;}}if(!flag){puts("IMPOSSIBLE");continue;}if(sum/(h+1)%(v+1)!=0){puts("IMPOSSIBLE");continue;}t=sum/(h+1)/(v+1);memset(cnts,0,sizeof(int)*(2+h));flag=1;for(int i=1;i<=m;++i){for(int j=1;j<=n;++j){cnts[hq[j]]+=(a[j][i]=='@');}if(check1()){memset(cnts,0,sizeof(int)*(2+h));}else if(!check2()){flag=0;break;}}if(!flag){puts("IMPOSSIBLE");continue;}puts("POSSIBLE");}return 0;
}