洛谷P3585 [POI2015]PIE

传送门

题目大意:有个n*m的格子图,要求'x'点要被染成黑色

有个a*b的印章,'x'是可以染色的印章上的点。

要求用印章去染色格子

(1)印章不可以旋转。

(2)不能把墨水印到纸外面。

(3)纸上的同一个格子不可以印多次。

题解:模拟

从题目中可以看出,一定要让印章的左上角对应目前n*m方

格中未染色的左上角。因为要求不能重复染色,可以每染完

一个格子就把它赋值为0.(待染色为1)。

开始纯模拟,没有任何优化的代码。

加了个读入优化还是T了两个点,3000ms+

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1022
using namespace std;
int n,m,a,b,fa,fb,cnt,q;
int map[N][N],yz[N][N];
char s[N];
inline int read(int &x){
    char ch=getchar();x=0;int f=1;
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    x=x*f;
}
void init(){
    memset(map,0,sizeof(map));
    memset(yz,0,sizeof(yz));
    cnt=0;fa=0;fb=0;
}
bool check(int x,int y){
    int xx=x-fa,yy=y-fb;
    for(int i=1;i<=a;i++){
        for(int j=1;j<=b;j++){
            if(yz[i][j]==0)continue;
            int rx=xx+i,ry=yy+j;
            if(rx<0||ry<0||rx>n||ry>m||map[rx][ry]==0)return false;
            map[rx][ry]=0;cnt--;
        }
    }
    return true;
}
int main(){
    scanf("%d",&q);
    while(q--){
        init();bool flag=false;
       // scanf("%d%d%d%d",&n,&m,&a,&b);
      //  n=read();m=read();a=read();b=read();
       read(n);read(m);read(a);read(b);
        for(int i=1;i<=n;i++){
            scanf("%s",s+1);
            for(int j=1;j<=m;j++)
             if(s[j]=='x')map[i][j]=1,cnt++;
        }
        for(int i=1;i<=a;i++){
            scanf("%s",s+1);
            for(int j=1;j<=b;j++){
                if(s[j]=='.')continue;
                if(!fa&&!fb)fa=i,fb=j;
                yz[i][j]=1;
            }
        }
        for(register int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(map[i][j]){
                    if(check(i,j)==0){
                        printf("NIE
");
                        flag=true;break;
                    }
                    if(cnt==0){
                        printf("TAK
");
                        flag=true;break;
                    }
                }
            }
            if(flag)break;
        }
    }
    return 0;
}
80

看了题解... 想到以前做靶型数独这个题,把未填数的格子放到一个结构体里。

w[i].x,w[i].y分别表示第i个没有填数格子的横纵坐标。

这样的好处是不用遍历整张图,就找到了没填数的格子。

这个题也是这样....

上面的代码不仅遍历了一遍要染色的图,还遍历了整个印章。

最差的情况是10^12...遍历要染色的10^6,印章10^6。

所以把要染色的点和能染色的点抽离出来,放到结构体里。

很好的一个优化,188ms。

ps:某一行后面+***,可以这样理解..

yz[1].x+xx=x,yz[1].y+yy=y.

说明印章的左上角的可以染色的点,要对应

n*m的棋盘要加xx和yy,那么其他可以染色的点也要加这两个数

来对应他们要染色的点。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1009
using namespace std;
int n,m,a,b,cnt_black,yz_black,q;
char s[N];
int map[N][N];
struct Make_Black{
    int x,y;
}gz[N*N],yz[N*N];
bool check(int x,int y){
    int xx=x-yz[1].x,yy=y-yz[1].y;  //*** 
    for(int i=1;i<=yz_black;i++){  
        int nx=yz[i].x+xx,ny=yz[i].y+yy;
        if(nx<0||nx>n||ny<0||ny>m||map[nx][ny]==0)return false;
        map[nx][ny]=false;
    }
    return true;
}
int main(){
    scanf("%d",&q);
    while(q--){
        bool flag=false;
        cnt_black=yz_black=0;
        scanf("%d%d%d%d",&n,&m,&a,&b);
        memset(map,0,sizeof(map));
        for(int i=1;i<=n;i++){
            scanf("%s",s+1);
            for(int j=1;j<=m;j++)
             if(s[j]=='x'){
                 gz[++cnt_black].x=i;gz[cnt_black].y=j;
                 map[i][j]=true;
             }
        }
        for(int i=1;i<=a;i++){
            scanf("%s",s+1);
            for(int j=1;j<=b;j++)
             if(s[j]=='x')yz[++yz_black].x=i,yz[yz_black].y=j;
        }
        for(int i=1;i<=cnt_black;i++){
            if(map[gz[i].x][gz[i].y])
             if(check(gz[i].x,gz[i].y)==0){
                 flag=true;
                 printf("NIE
");break;
             }
        }
        if(!flag)printf("TAK
");
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/zzyh/p/7788275.html