=== ===
这里放传送门
=== ===
题解
这题我记得是我放了假去理发排队的时候正好爸爸车上有我的笔记本电脑然后就拿出来把这题写了结果都轮到我理发了好像还没写完2333
这道题的意思是给出一个网格和可用的颜色数量,然后给出每相邻两个格子的约束条件,要求构造一组方案,使得没有被满足的约束条件不超过所有条件的1/4。
考虑如何才能让不被满足的条件不超过1/4。假设网格是n*m的,那么如果不考虑竖着的那n个限制,横着的m个限制是一定能够被全部满足的。要满足“横着的”限制,显然就是一列一列的填格子,因为“横着的”限制只跟上下两个格子有关。并且可以只用两种颜色来回填。那么当不考虑“竖着的”限制的时候,“横着的”限制就可以被全部满足,那么如果让n和m里面较大的那一个当做“横着的”限制,那么满足这些以后就已经满足了所有限制的1/2。然后再考虑满足“竖着的”限制,仍然是一列一列的看。如果能保证每一列上没有被满足的“竖着的”限制不超过这一列的1/2,那么因为本身“竖着的”限制就比“横着的”限制少,就算所有“竖着的”限制只满足了一半,加上那些已经被满足了的“横着的”限制也最少就是3/4。那么当扫描到某一列的时候发现它跟它旁边那一列的冲突数量超过了总数的1/2,就把这一列的格子颜色全部取反。取反以后横着的限制仍然是满足的,但竖着的限制状态都取反了,那么没有被满足的数量就可以被控制在1/2以内,就可以满足要求了。
这道题比较麻烦的地方在于预处理啊。。因为它读入本来就很麻烦,并且还要保证“横着的”限制多于“竖着的”限制,否则的话还要把网格转一下。。并且还有一个问题就是因为上面说的所有的前提都是可用颜色数量大于等于2,因为满足“横着的”限制的时候是用两种颜色倒着填的。不过只能用一种颜色的时候也好办,统计一下不等号的数量就可以了。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int h,w,k,r1,r2,c1,c2,clr[1010][1010],N,tot,unf;
bool r[1010][1010],c[1010][1010];
char get(){
char ch=getchar();
while (ch!='N'&&ch!='E') ch=getchar();
return ch;
}
void Swaping(){
bool tmp[1010][1010];
memcpy(tmp,r,sizeof(r));
memset(r,false,sizeof(r));
for (int i=1;i<=c1;i++)
for (int j=1;j<=c2;j++)
r[j][i]=c[i][j];一列列读c数组放到r里
memset(c,false,sizeof(c));
for (int i=1;i<=r1;i++)
for (int j=1;j<=r2;j++)
c[j][i]=tmp[i][j];
swap(h,w);N=1;//打上交换标记
r1=h-1;r2=w;c1=h;c2=w-1;//注意记录数组规模的变量也要随着交换
return;
}
int main()
{
scanf("%d%d%d",&h,&w,&k);
for (int i=1;i<=2*h-1;i++){
int x=(i&1)?w-1:w;
if (x==w-1) ++c1;
else ++r1;
for (int j=1;j<=x;j++){
char ch=get();
if (ch=='N') ++N;//为了特判k=1的情况而记录N的个数
++tot;
if (x==w-1) c[c1][j]=(ch=='E');
else r[r1][j]=(ch=='E');
}
}
r2=w;c2=w-1;
if (k==1){
if (N<=tot/4){
printf("YES\n");
for (int i=1;i<=h;i++)
for (int j=1;j<=w;j++)
printf("1%c"," \n"[j==w]);
}else printf("NO\n");
return 0;
}
N=0;
if (r1*r2<c1*c2) Swaping();//如果列上的要求比行上的要求多就交换
memset(clr,-1,sizeof(clr));
for (int i=1;i<=r2;i++) clr[1][i]=0;//每一行都是和它上面的一行进行匹配
for (int j=1;j<=r2;j++)//所以第一行可以全部填0
for (int i=2;i<=r1+1;i++)
if (r[i-1][j]==false) clr[i][j]=clr[i-1][j]^1;
else clr[i][j]=clr[i-1][j];
for (int j=2;j<=c2+1;j++){
int cnt=0;
for (int i=1;i<=c1;i++)
if (clr[i][j]==clr[i][j-1])//每一列和它左边的一列进行匹配
cnt+=(c[i][j-1]==false);
else cnt+=(c[i][j-1]==true);
if (cnt>h/2)
for (int i=1;i<=c1;i++)
clr[i][j]=clr[i][j]^1;
}
printf("YES\n");
if (N!=0)//如果交换过就改变输出顺序
for (int i=1;i<=w;i++)
for (int j=1;j<=h;j++)
printf("%d%c",clr[j][i]+1," \n"[j==h]);
else
for (int i=1;i<=h;i++)
for (int j=1;j<=w;j++)
printf("%d%c",clr[i][j]+1," \n"[j==w]);
printf("\n");
return 0;
}
偏偏在最后出现的补充说明
构造题的思路方法和普通的题感觉好不一样啊。。还是要多考虑一下它的性质啊。