感触:(不敢说是思路)
网上说此题最后一个点卡常数,,dfs肯定跑不快,这不明显坑爹吗!~
网上有大神和我写的差不多的,各种优化,最关键人家是用bfs搜过的。
dfs改bfs,bfs不知道哪写错了。
所有答案都WA了。(bfs部分引去了)
挑了1个多小时,实在调不出来了。放弃了。。。
~~~~(>_<)~~~~ 又舍不得扔。
90分代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; inline const int read(){ register int x=0,f=1; register char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); return ch-'0'; } const int dx[]={0,0,1,-1}; const int dy[]={1,-1,0,0}; const int N=9; int n,g[N][N],a[N][N]; bool flag,vis[N][N],mark[N]; inline void calc(int &c){ memset(mark,0,sizeof mark); c=0; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(!mark[a[i][j]]){ mark[a[i][j]]=1; c++; } } } } void go(int x,int y,int c,int r){ a[x][y]=r; vis[x][y]=1; for(int i=0;i<4;i++){ int nx=x+dx[i]; int ny=y+dy[i]; if(!vis[nx][ny]&&nx>0&&nx<=n&&ny>0&&ny<=n&&a[nx][ny]==c){ go(nx,ny,c,r); } } } void get_round(int x,int y,int c,int round[]){ vis[x][y]=1; for(int i=0;i<4;i++){ int nx=x+dx[i]; int ny=y+dy[i]; if(!vis[nx][ny]&&nx>0&&nx<=n&&ny>0&&ny<=n){ if(a[nx][ny]==c) get_round(nx,ny,c,round); else if(!round[a[nx][ny]]) round[a[nx][ny]]=1; } } } /* #define xx first #define yy second pair<int,int>q[800000]; void go(int x,int y,int c,int r){ a[x][y]=r; vis[x][y]=1; int h=0,t=1; while(h!=t){ ++h; for(int i=0;i<4;i++){ int nx=q[h].xx; int ny=q[h].yy; if(!vis[nx][ny]&&nx>0&&nx<=n&&ny>0&&ny<=n&&a[nx][ny]==c){ vis[nx][ny]=1; a[nx][ny]=r; ++t; q[t]=make_pair(nx,ny); } } } } void get_round(int x,int y,int c,int round[]){ vis[x][y]=1; int h=0,t=1; while(h!=t){ ++h; for(int i=0;i<4;i++){ int nx=q[h].xx; int ny=q[h].yy; if(!vis[nx][ny]&&nx>0&&nx<=n&&ny>0&&ny<=n){ if(a[nx][ny]==c){ vis[nx][ny]=1; ++t; q[t]=make_pair(nx,ny); } else if(!round[a[nx][ny]]) round[a[nx][ny]]=1; } } } }*/ void dfs(int now,int sum){ if(flag) return ; int C; calc(C); if(now==sum){ if(C==1) flag=1; return ; } if(now+C-1>sum) return ; memset(vis,0,sizeof vis); int round[N]={0}; get_round(1,1,a[1][1],round); for(int i=0;i<=5;i++){ if(i==a[1][1]||!round[i]) continue; int back[N][N]; memcpy(back,a,sizeof a); memset(vis,0,sizeof vis); go(1,1,a[1][1],i); if(flag) return ; dfs(now+1,sum); memcpy(a,back,sizeof back); } } int main(){ freopen("sh.txt","r",stdin); for(;;){ n=read(); if(!n) break; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ g[i][j]=read(); } } int ans=0; for(int k=0;k<=16;k++){ memcpy(a,g,sizeof g); flag=0; dfs(0,k); if(flag){ ans=k; break; } } printf("%d ",ans); } return 0; }
AC的艰辛:
题解:
主要是优化了我的“大水漫灌”函数:(基本上都改了)
我们可以发现,每次寻找左上角的格子所在的联通块耗费的时间常数巨大。因此我们在这里寻求突破。
我们引入一个N*N的v数组。左上角的格子所在的联通块里的格子标记为1。左上角联通块周围一圈格子标记为2,其它格子标记为0。如果某次选择了颜色c,我们只需要找出标记为2并且颜色为c的格子,向四周扩展,并相应地修改v标记,就可以不断扩大标记为1的区域,最终如果所有格子标记都是1,那么显然找到了答案。(参考黄学长的博客)
AC代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int dx[]={0,0,1,-1}; const int dy[]={1,-1,0,0}; const int N=9; int n,mp[N][N]; int mark[N][N]; bool ans,vis[N]; int s; inline int get(){ int t=0; memset(vis,0,sizeof vis); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(!vis[mp[i][j]]&&mark[i][j]!=1){ vis[mp[i][j]]=1; t++; } } } return t; } void dfs(int x,int y,int val){ mark[x][y]=1; for(int i=0;i<4;i++){ int nx=x+dx[i]; int ny=y+dy[i]; if(nx<1||nx>n||ny<1||ny>n||mark[nx][ny]==1) continue; mark[nx][ny]=2; if(mp[nx][ny]==val) dfs(nx,ny,val); } } inline int fill(int x){ int t=0; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(mark[i][j]==2&&mp[i][j]==x){ t++; dfs(i,j,x); } } } return t; } void search(int k){ int v=get(); if(!v) ans=1; if(k+v>s||ans) return ; int tmp[N][N]; for(int i=0;i<=5;i++){ memcpy(tmp,mark,sizeof mark); if(fill(i)) search(k+1); memcpy(mark,tmp,sizeof mark); } } int main(){ freopen("sh.txt","r",stdin); for(scanf("%d",&n);n;scanf("%d",&n)){ memset(mark,0,sizeof mark); ans=0; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ scanf("%d",&mp[i][j]); } } dfs(1,1,mp[1][1]); for(s=0;;s++){ search(0); if(ans){printf("%d ",s);break;} } } return 0; }