POJ3279-Fliptile
继续刷邝斌飞搜索专题
首次写完草稿
1 //搜索比数论简单太多了,刷多了人容易傻 2 //邝斌之前博客签名人一我十,人十我百,分类真的好啊 3 //感觉上一个题正好给这个题做铺垫,但上一个是点状态,这个是二维数组状态,又不会了,状态数组究竟是string还是int的二维数组,而我又很烦很弱项字符串那堆玩意,结构体内究竟要不要存一个map实时数组,写的我辗转反侧 4 //这群傻逼为啥总用黑色呢,看得我眼花又不明显,之前大学装逼用绿色黑色发现都挺反效果,不如默认白色。tg说黑色为了高亮明显 5 6 7 //这题什么定位啊操,真给我写崩溃了 8 #include<stdio.h> 9 #include<iostream> 10 #include<string.h> 11 using namespace std; 12 #include<queue> 13 int dir[5][2]={{0,0},{-1,0},{1,0},{0,-1},{0,1}}; 14 int map[15][15];//此处不再纠结按照V8说的多开5个 15 struct State{ 16 int count;//(x,y)这个点变化了几次,不是BFS里的最少步骤 17 int realtime_map[15][15];//(x.y)这个点变化使得map更新为新地图 18 }; 19 State state; 20 queue<State>q; 21 int M,N; 22 int legal(int x,int y) 23 { 24 if(0<x&&x<M&&0<y&&y<N) 25 return 1; 26 else 27 return 0; 28 } 29 //struct Vis{ 30 // int rmap[15][15]; 31 // int v; 32 //}vis;//妈的还是不对,想起状态压缩去回顾下 33 ////string长度为225的字符串来存也不行,变幻一次就得存一下,如果变换1000次,就得有1000个长度为225的string 34 ////而且我在想,15*15反转,真的有穷个吗? 35 ////突然回忆V8讲种树还是什么,大概是说比如19 27走过就vis[19][27]=1,但其他都空着,说可以状态压缩 36 ////二哥网名树状DP/树状数组/数位dp/树形dp https://blog.csdn.net/weixin_45697774/article/details/104404768 37 ////好像是后缀数组 38 //https://zhuanlan.zhihu.com/p/80439826 39 40 string vis[300];//打算做最多翻转300次,但发现没带判断是否存在,但没找都如果检查是否存在用啥东西,直接笨写遍历比较了 41 char bot; 42 State thisstate; 43 State nextstate; 44 45 int canfind; 46 int alliszero()//直接用N*M做string比较在if里也行 47 { 48 for(int i=0;i<M;i++) 49 for(int j=0;i<N;j++){ 50 if(thisstate.realtime_map[i][j]==map[i][j]) 51 ; 52 else 53 return 0; 54 } 55 return 1; 56 } 57 int main() 58 { 59 60 while(cin>>M>>N){ 61 canfind=0; 62 memset(vis,0,sizeof(vis)); 63 for(int i=0;i<M;i++) 64 for(int j=0;j<N;j++) 65 cin>>map[i][j]; 66 state.count=0; 67 for(int i=0;i<M;i++) 68 for(int j=0;j<N;j++){ 69 state.realtime_map[i][j]=map[i][j]; 70 bot='0'+map[i][j]; 71 vis[0]=vis[0]+bot; 72 } 73 q.push(state); 74 while(!q.empty()){ 75 thisstate=q.front(); 76 q.pop(); 77 78 if(alliszero()){ 79 canfind=1; 80 while(!q.empty()) 81 q.pop(); 82 // 开始打印 83 cout<<"find!!"<<endl; 84 break; 85 } 86 87 // nextstate.count=thisstate.count+1;//不可以这么写 88 nextstate.count=thisstate.count++; 89 for(int i=0;i<M;i++) 90 for(int j=0;j<N;j++){//起手map先赋值为一模一样 91 nextstate.realtime_map[i][j]=thisstate.realtime_map[i][j]; 92 bot='0'+thisstate.realtime_map[i][j]; 93 vis[nextstate.count]=vis[nextstate.count]+bot; 94 } 95 //自己写,就来回推翻重写or反复增增减减,重新定义变量,各种问题, 96 //诸如结构体里都定义什么,vis怎么存,想破脑袋来来回回把所有错误的都踩一遍才有所长进 97 //不然看了别人代码记住了,再变个型又不会了,只有自己写排除所有错误思路找到正确写法 98 //才是提高代码能力关键 99 for(int i=0;i<M;i++){ 100 for(int j=0;j<N;j++){//遍历把每一个元素都压入队列 101 102 for(int k=0;k<5;k++)//做反转 103 if(legal(i+dir[k][0],j+dir[k][1])){ 104 int fliptile=(thisstate.realtime_map[i+dir[k][0]][j+dir[k][1]]+1)%2;//记得西安艾教还是谁教的,+1再模2,就是是把0/1翻转 105 nextstate.realtime_map[i+dir[k][0]][j+dir[k][1]]=fliptile; 106 // vis[nextstate.count].replace ((i+dir[k][0])*N+(j+dir[k][1]),1, '0'+fliptile);//这块怎么似曾相识v8讲过呢poj黑白棋盘还是灯泡01 107 string str = to_string(fliptile);//东拼西凑到处用到啥现学现查总算没报错了,但无法像snow一样,工作级别难度现学,也无法知道测试都知道的接口。傻逼测试. 108 // 写完调都不想调,哎,都不知道自己写的方向对不对,而且这如果不是什么难题,我也太受打击了,可如果是,那我又飘了, 109 vis[nextstate.count].replace ((i+dir[k][0])*N+(j+dir[k][1]),1, str);//这块怎么似曾相识v8讲过呢poj黑白棋盘还是灯泡01 110 }//至此地图状态都记录完了 111 112 // //记录状态 113 // for(int i=0;i<M;i++) 114 // for(int j=0;j<N;j++){ 115 // vis.rmap[i][j]=nextstate.realtime_map[i][j]; 116 // vis.v=1; 117 // } 118 for(int i=0;i<nextstate.count;i++){//感觉自己代码写的像打补丁,好蠢好笨 119 string s1; 120 if(s1.compare(vis[nextstate.count])==0) 121 break; 122 } 123 if(i==nextstate.count)//证明没break过,状态没出现过 124 {//那就加入队列,并把状态更新 125 q.push(nextstate); 126 } 127 else//证明break出来了,那此次的nextcout就不生效了是没用的,减回去,腾出地方等着下一次反转再用 128 thisstate.count--; 129 130 } 131 } 132 } 133 if(canfind==0) 134 cout<<"imposs"<<endl; 135 } 136 } 137 //////练习: 138 ////string vis[300][225];//假设最多翻转300次 139 ////以为是300个长度为225的字符串,跟char搞混了 140 //string vis[300]; 141 //int main() 142 //{ 143 //// vis[0]="SDFS"; 144 //// cout<<vis[0]; 145 //// memset(vis,0,sizeof(vis));//不可以这么用 146 //// strcat(vis,"re"); 147 //int a=3; 148 //char b=a+'0'; 149 // vis[0]=vis[0]+b; 150 // cout<<vis[0]<<"#"<<endl;; 151 //}
逐行检验,一行一行调试,小笔误就不写了,写下出错点
1、回想起刷这题刚知道的,string不应该用memset,改下
2、for循环里的 i j 笔误
3、88行的i++/++i问题
4、legal函数里笔误,x/y应该是大于等于0且小于M/N
5、128的 count-- 写完,109行没++,遍历其他点做翻转的时候应该++的,做个改正,把109行的做个+1
修改完感觉没问题
1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 using namespace std; 5 #include<queue> 6 int dir[5][2]={{0,0},{-1,0},{1,0},{0,-1},{0,1}}; 7 int map[15][15]; 8 struct State{ 9 int count; 10 int realtime_map[15][15]; 11 }; 12 State state; 13 queue<State>q; 14 int M,N; 15 int legal(int x,int y) 16 { 17 if(0<=x&&x<M&&0<=y&&y<N) 18 return 1; 19 else 20 return 0; 21 } 22 char bot; 23 State thisstate; 24 State nextstate; 25 26 int canfind; 27 int alliszero() 28 { 29 for(int i=0;i<M;i++) 30 for(int j=0;j<N;j++){ 31 if(thisstate.realtime_map[i][j]==0) 32 ; 33 else 34 return 0; 35 } 36 return 1; 37 } 38 int ans[15][15]; 39 40 int main() 41 { 42 43 while(cin>>M>>N){ 44 canfind=0; 45 string vis[300]={}; 46 memset(ans,0,sizeof(ans)); 47 for(int i=0;i<M;i++) 48 for(int j=0;j<N;j++) 49 cin>>map[i][j]; 50 51 52 state.count=0; 53 for(int i=0;i<M;i++) 54 for(int j=0;j<N;j++){ 55 state.realtime_map[i][j]=map[i][j]; 56 bot='0'+map[i][j]; 57 vis[0]=vis[0]+bot; 58 } 59 60 q.push(state); 61 while(!q.empty()){ 62 thisstate=q.front(); 63 q.pop(); 64 65 if(alliszero()){ 66 canfind=1; 67 while(!q.empty()) 68 q.pop(); 69 for(int i=0;i<M;i++) 70 for(int j=0;j<N;j++){ 71 cout<<ans[i][j]; 72 if(j!=N-1) 73 cout<<" "; 74 if(j==N-1) 75 cout<<endl; 76 } 77 break; 78 } 79 nextstate.count=++thisstate.count; 80 for(int i=0;i<M;i++) 81 for(int j=0;j<N;j++){ 82 nextstate.realtime_map[i][j]=thisstate.realtime_map[i][j]; 83 bot='0'+thisstate.realtime_map[i][j]; 84 vis[nextstate.count]=vis[nextstate.count]+bot; 85 } 86 87 88 89 for(int i=0;i<M;i++){ 90 for(int j=0;j<N;j++){ 91 92 for(int k=0;k<5;k++) 93 if(legal(i+dir[k][0],j+dir[k][1])){ 94 int fliptile=(thisstate.realtime_map[i+dir[k][0]][j+dir[k][1]]+1)%2; 95 nextstate.realtime_map[i+dir[k][0]][j+dir[k][1]]=fliptile; 96 string str = to_string(fliptile); 97 vis[++nextstate.count].replace((i+dir[k][0])*N+(j+dir[k][1]),1, str); 98 } 99 100 101 102 for(int i=0;i<nextstate.count;i++){ 103 string s1; 104 if(s1.compare(vis[nextstate.count])==0) 105 break; 106 } 107 if(i==nextstate.count){ 108 q.push(nextstate); 109 ans[i][j]++; 110 } 111 else 112 thisstate.count--; 113 } 114 } 115 } 116 if(canfind==0) 117 cout<<"IMPOSSIBLE"<<endl; 118 } 119 }
但报了个错,第一次遇到控制台也会报错(查说是异常)

没去管他(说是什么访问下标抛出异常,看不懂,但后来调试其他的发现是因为in写错导致count--,然后再次循环把值赋给了负数下标的string数组,类似vis[nextstate.count],但其实count是-1),发现错误点5还是有问题,重新写 nextstate.count 的代码逻辑,改后新代码
1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 using namespace std; 5 #include<queue> 6 int dir[5][2]={{0,0},{-1,0},{1,0},{0,-1},{0,1}}; 7 int map[15][15]; 8 struct State{ 9 int count; 10 int realtime_map[15][15]; 11 }; 12 State state; 13 queue<State>q; 14 int M,N; 15 int legal(int x,int y) 16 { 17 if(0<=x&&x<M&&0<=y&&y<N) 18 return 1; 19 else 20 return 0; 21 } 22 char bot; 23 State thisstate; 24 State nextstate; 25 26 int canfind; 27 int alliszero() 28 { 29 for(int i=0;i<M;i++) 30 for(int j=0;j<N;j++){ 31 if(thisstate.realtime_map[i][j]==0) 32 ; 33 else 34 return 0; 35 } 36 return 1; 37 } 38 int ans[15][15]; 39 40 int main() 41 { 42 43 while(cin>>M>>N){ 44 canfind=0; 45 string vis[300]={}; 46 memset(ans,0,sizeof(ans)); 47 for(int i=0;i<M;i++) 48 for(int j=0;j<N;j++) 49 cin>>map[i][j]; 50 51 52 state.count=0; 53 for(int i=0;i<M;i++) 54 for(int j=0;j<N;j++){ 55 state.realtime_map[i][j]=map[i][j]; 56 bot='0'+map[i][j]; 57 vis[0]=vis[0]+bot; 58 } 59 60 q.push(state); 61 while(!q.empty()){ 62 thisstate=q.front(); 63 q.pop(); 64 65 if(alliszero()){ 66 canfind=1; 67 while(!q.empty()) 68 q.pop(); 69 for(int i=0;i<M;i++) 70 for(int j=0;j<N;j++){ 71 cout<<ans[i][j]; 72 if(j!=N-1) 73 cout<<" "; 74 if(j==N-1) 75 cout<<endl; 76 } 77 break; 78 } 79 nextstate.count=thisstate.count; 80 for(int i=0;i<M;i++) 81 for(int j=0;j<N;j++){ 82 nextstate.realtime_map[i][j]=thisstate.realtime_map[i][j]; 83 bot='0'+thisstate.realtime_map[i][j]; 84 vis[++nextstate.count]=vis[nextstate.count]+bot; 85 } 86 87 88 89 for(int i=0;i<M;i++){ 90 for(int j=0;j<N;j++){ 91 92 for(int k=0;k<5;k++) 93 if(legal(i+dir[k][0],j+dir[k][1])){ 94 int fliptile=(thisstate.realtime_map[i+dir[k][0]][j+dir[k][1]]+1)%2; 95 nextstate.realtime_map[i+dir[k][0]][j+dir[k][1]]=fliptile; 96 string str = to_string(fliptile); 97 vis[nextstate.count].replace((i+dir[k][0])*N+(j+dir[k][1]),1, str); 98 } 99 100 101 102 for(int i=0;i<nextstate.count;i++){ 103 string s1; 104 if(s1.compare(vis[nextstate.count])==0) 105 break; 106 } 107 if(i==nextstate.count){ 108 q.push(nextstate); 109 ans[i][j]++; 110 } 111 else 112 nextstate.count--; 113 } 114 } 115 } 116 if(canfind==0) 117 cout<<"IMPOSSIBLE"<<endl; 118 } 119 }
运行样例输出impossible
发现for循环里118行用了同样的i变量,重名。下面123行的i也是指代不明,换个名
而且119行的s1忘记赋值了
123行本想用如果break就是状态有出现过,放弃压入队列,然后用k是不是最大那个,但其实此时k就已经未定义了,是for循环里的
还是运行样例输出impossible
崩溃了,此时发现ans不太对,这样只要是没做够过就都ans[i][j]++了,但根本没考虑是不是正确翻转路径上的答案啊。有点打印路径那个题的意思。
还有count应该是不需要,推翻重写这块的逻辑
in初始值也笔误了
vis重写代码逻辑
PS:

像这样打印输出,“夹逼定理”,循环每次都是输出固定三种符号,到哪不输出了,就知道哪里是抛除异常了,记录下找bug的方法
更新改后代码,还是不对
1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 using namespace std; 5 #include<queue> 6 int dir[5][2]={{0,0},{-1,0},{1,0},{0,-1},{0,1}}; 7 int map[15][15]; 8 int count; 9 struct State{ 10 string ans; 11 int realtime_map[15][15]; 12 }; 13 State firststate; 14 queue<State>q; 15 int M,N; 16 int legal(int x,int y) 17 { 18 if(0<=x&&x<M&&0<=y&&y<N) 19 return 1; 20 else 21 return 0; 22 } 23 char bot; 24 State thisstate; 25 State nextstate; 26 27 int canfind; 28 int alliszero() 29 { 30 for(int i=0;i<M;i++) 31 for(int j=0;j<N;j++){ 32 if(thisstate.realtime_map[i][j]==0) 33 ; 34 else 35 return 0; 36 } 37 return 1; 38 } 39 int main() 40 { 41 // freopen("zhishu.txt","r",stdin); 42 while(cin>>M>>N){ 43 count=0; 44 canfind=0; 45 string vis[9000]={}; 46 for(int i=0;i<M;i++) 47 for(int j=0;j<N;j++) 48 cin>>map[i][j]; 49 50 51 for(int i=0;i<M;i++) 52 for(int j=0;j<N;j++){ 53 firststate.realtime_map[i][j]=map[i][j]; 54 bot='0'+map[i][j]; 55 vis[0]=vis[0]+bot; 56 } 57 58 for(int i=0;i<M;i++) 59 for(int j=0;j<N;j++) 60 firststate.ans=firststate.ans+'0'; 61 // cout<<firststate.ans<<endl; 62 63 q.push(firststate); 64 while(!q.empty()){ 65 thisstate=q.front(); 66 q.pop(); 67 68 if(alliszero()){ 69 canfind=1; 70 while(!q.empty()) 71 q.pop(); 72 for(int i=0;i<M*N;i++){ 73 cout<<thisstate.ans[i]; 74 if((i+1)%4==0) 75 cout<<endl; 76 else 77 cout<<" "; 78 } 79 break; 80 } 81 count++; 82 for(int i=0;i<M;i++) 83 for(int j=0;j<N;j++){ 84 nextstate.realtime_map[i][j]=thisstate.realtime_map[i][j]; 85 bot='0'+thisstate.realtime_map[i][j]; 86 vis[count]=vis[count]+bot; 87 } 88 for(int i=0;i<M;i++) 89 for(int j=0;j<N;j++){ 90 for(int k=0;k<5;k++) 91 if(legal(i+dir[k][0],j+dir[k][1])){ 92 int fliptile=(thisstate.realtime_map[i+dir[k][0]][j+dir[k][1]]+1)%2; 93 nextstate.realtime_map[i+dir[k][0]][j+dir[k][1]]=fliptile; 94 string str = to_string(fliptile); 95 vis[count].replace((i+dir[k][0])*N+(j+dir[k][1]),1, str); 96 } 97 98 int in=0; 99 for(int k=0;k<count;k++){ 100 string s1=vis[k]; 101 if(s1.compare(vis[count])==0){ 102 in=1; 103 break; 104 } 105 } 106 if(in==0){ 107 string ss= thisstate.ans.substr(i*N+j, 1); 108 int num=stoi(ss); 109 num++; 110 string sss = to_string(num); 111 nextstate.ans=thisstate.ans; 112 nextstate.ans.replace(i*N+j,1,sss); 113 q.push(nextstate); 114 count++; 115 } 116 else 117 ; 118 } 119 } 120 if(canfind==0) 121 cout<<"IMPOSSIBLE"<<endl; 122 } 123 }
回忆知乎评论各大比赛难度排序。百度搜“洛谷省选是acm什么水平”安慰自己
ans不难,现在就剩下状态存储了,就是vis和count这块,81~114行,把thisstate此时状态先赋值给nextstate,然后再循环遍历整个地图,对于每一次遍历,也就是地图上每一个点,都再for(0~4)遍历一下,也就是翻转。意思就是说,此时状态,我做一步操作,下一步都可以获得哪些状态,那一定是此时状态的基础上把所有地图的点都翻转一次,
比如3 3
a b c
d e f
g h i
下一步可以是a翻转,可以是b翻转...可以i翻转
我没想到什么更好的办法去写,就只能先thisstate全赋值,再一个一个更新,但这样问题太多了,最大问题就是count++后,下一次的vis[count]没赋值就replace,推翻重新写这块
太复杂了,写注释能+用函数好写点,for一多就有点乱了
要么2个for定位到一个点后,内层再2个for遍历判断如果遍历到的点是外层点的邻边就翻转,否则就直接赋值,但对于同一个thisstate,除了遍历到的某点应该反转,其他都是不用反转直接赋值,那就多了好多重复赋值,写都懒得写这种
我是直接2个for把this全赋值给nextstate,然后再新开2个for遍历所有点,每个点再for0~4直接replace翻转,因为之前搞了个dir。这就是我第一次写的方法,但发现有问题,2个for把this全赋值给nextstate是对于每个thisstate只执行一次,下面新开的2个for遍历所有点,每个点再for0~4直接翻转,只有第一次可以,之后count++后,vis里就没值了,没法replace,感觉再赋值一次有点der,不知道会不会像之前一样,以为der的都是正解。
憋了半天,跟发明算法似得
1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 using namespace std; 5 #include<queue> 6 int dir[5][2]={{0,0},{-1,0},{1,0},{0,-1},{0,1}}; 7 int map[15][15]; 8 int count; 9 struct State{ 10 string ans; 11 int realtime_map[15][15]; 12 }; 13 int num_of_bfs; 14 State firststate; 15 queue<State>q; 16 int M,N; 17 int legal(int x,int y) 18 { 19 if(0<=x&&x<M&&0<=y&&y<N) 20 return 1; 21 else 22 return 0; 23 } 24 char bot; 25 State thisstate; 26 State nextstate; 27 28 int canfind; 29 int alliszero() 30 { 31 for(int i=0;i<M;i++) 32 for(int j=0;j<N;j++){ 33 if(nextstate.realtime_map[i][j]==0) 34 ; 35 else 36 return 0; 37 } 38 return 1; 39 } 40 string vis[9000]; 41 void fliptile(int x,int y); 42 void readyforfliptile(); 43 int main() 44 { 45 freopen("zhishu.txt","r",stdin); 46 while(cin>>M>>N){ 47 count=0; 48 canfind=0; 49 vis[900]={};//这里为什么string只能开900,而9000就卡住,之前string vis声明+定义都在这main里就没事卡伊开9000 50 for(int i=0;i<M;i++) 51 for(int j=0;j<N;j++) 52 cin>>map[i][j];//存图 53 54 55 for(int i=0;i<M;i++) 56 for(int j=0;j<N;j++){ 57 firststate.realtime_map[i][j]=map[i][j];//记录首次状态 58 firststate.ans=firststate.ans+'0';//记录首次,每个点,需要反转几次 59 bot='0'+map[i][j]; 60 vis[0]=vis[0]+bot; 61 } 62 q.push(firststate); 63 count++;//放里面对多加一次 64 65 thisstate=q.front();//同上 66 readyforfliptile(); 67 68 while(!q.empty()){ 69 thisstate=q.front(); 70 q.pop(); 71 72 for(int i=0;i<M;i++) 73 for(int j=0;j<N;j++){//负责遍历搜索每一个点 74 if(canfind==0) 75 fliptile(i,j);//对于该点做翻转,并且,如果没出现过的状态就压入队列 76 if(alliszero()&&canfind==0){//翻成全是0就输出 77 canfind=1; 78 while(!q.empty()) 79 q.pop(); 80 for(int i=0;i<M*N;i++){ 81 cout<<nextstate.ans[i]<<" "; 82 if((i+1)%4==0) 83 cout<<endl; 84 else 85 cout<<" "; 86 } 87 cout<<num_of_bfs<<endl; 88 break; 89 } 90 91 readyforfliptile(); 92 } 93 } 94 if(canfind==0) 95 cout<<"IMPOSSIBLE"<<endl; 96 } 97 } 98 void fliptile(int x,int y) 99 { 100 for(int k=0;k<5;k++) 101 if(legal(x+dir[k][0],y+dir[k][1])){ 102 int fliptilenode=(thisstate.realtime_map[x+dir[k][0]][y+dir[k][1]]+1)%2; 103 nextstate.realtime_map[x+dir[k][0]][y+dir[k][1]]=fliptilenode; 104 string str = to_string(fliptilenode); 105 // if(canfind==1) 106 // cout<<"%"<<vis[count]<<"%"<<endl; 107 vis[count].replace((x+dir[k][0])*N+(y+dir[k][1]),1, str);//就是这句,alliszero了break了,没重新赋值 108 // if(canfind==1) 109 // cout<<"!"<<endl; 110 } 111 // cout<<"#"<<count<<" "<<vis[count]<<endl; 112 int in=0; 113 for(int L=0;L<count;L++){//检查状态出现过没 114 string s1=vis[L]; 115 if(s1.compare(vis[count])==0){ 116 in=1; 117 break; 118 } 119 } 120 if(in==0){//没出现过 121 string ss= thisstate.ans.substr(x*N+y, 1); 122 int num=stoi(ss); 123 num++; 124 string sss = to_string(num); 125 nextstate.ans=thisstate.ans; 126 nextstate.ans.replace(x*N+y,1,sss); 127 128 // for(int i=0;i<N;i++){ 129 // for(int j=0;j<M;j++) { 130 // cout<<nextstate.realtime_map[i][j]; 131 // } 132 // cout<<endl; 133 // } 134 // cout<<"("; 135 // for(int i=0;i<N;i++){ 136 // for(int j=0;j<M;j++){ 137 // cout<<thisstate.realtime_map[i][j]; 138 // } 139 // cout<<endl; 140 // } 141 // cout<<endl; 142 143 if(canfind==0){ 144 num_of_bfs++; 145 q.push(nextstate); 146 } 147 count++; 148 } 149 else 150 vis[count]={}; 151 152 } 153 void readyforfliptile() 154 { 155 for(int i=0;i<M;i++) 156 for(int j=0;j<N;j++){//先全体赋值,后面翻转时候只做替换改动 157 nextstate.realtime_map[i][j]=thisstate.realtime_map[i][j]; 158 bot='0'+thisstate.realtime_map[i][j]; 159 vis[count]=vis[count]+bot; 160 } 161 }
运行好久,且输出的是
0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0
不是最小字典序
这种情况是不是应该从后往前遍历
测试发现也不行,倒着答案也是一样的,看样子感觉是深搜啊艹
再来个找到不退出,整个string存所有最小值,一起输出,运行会更久
至此记录几个小问题。
这都是我花好几个小时调试才发现的问题,虽然算法根本不对,但会很好提高代码和debug能力
1、alliszero判断这块,一开始写的时候if里是thisstates,改nextstates可以剪枝
2、string等日后实际学到字符串再说吧,目前没查到什么解释,仅记录下问题:
全局定义9000vis string vis[9000]; ,main的while(cin>>M>>N)里再初始化9000string赋值为空串 vis[9000]={}; 程序会崩溃,900就没事 vis[900]={};
但900个串 string vis[900]={}; 定义加赋值都在这main里就没事,9000也行
操作发现,其实main赋值的时候,只要比全局定义的少就可以,全局如果定义9001,main里赋值空串string就可以是9000个了
搞不懂,先搁置,印象里几年前写代码就有过这个问题
3、count如果放在里面对导致再之后的q.front完count多加一次,因为fliptile函数也加了。下面那两个同上,如果放在里,vis会出现已经为N*M长度了的串,又重复来个字符串拼接
4、alliszero函数放在这是防止过多的fliptile,只要翻转找到了就别再翻转压入队列了,但实际对减少复杂度没啥帮助,复杂度后面说
5、遇到过两次命令行报异常“terminate called after throwing...”,第一次原因上面说了,第二次遇到是因为进入fliptile这个函数要判断下是否canfind是0,因为如果找到全0,break了,没执行到readyforfliptile赋值这句话,也就是此时count 是空的,可以输出但不能replace
1 //牛逼找到问题了 2 //访问没复制的就会有问题 3 #include <iostream> 4 #include <string> 5 #include <sstream> 6 using namespace std; 7 int main () 8 { 9 string a={}; 10 cout<<a[2]<<"#"; 11 a.replace(2,1, "s"); 12 }
整体思路:(对本题来说是错误思路,但既然想到就写完,提高debug和代码能力)
边写用到什么边回过头定义各种函数和声明
顺序看我代码没啥意义,因为一个月后估计我自己也看不懂,就说下思路
输入完N*M存图
存初始状态于vis里
存初始次数于起始结构体ans里(全0次),待压入队列
存初始map于结构体realtimemap里,待压入队列
之后的count和下面readyforfliptile可忽略,根据实际debug时打补丁一样的修改写法,单独拎出来没法说
然后正常BFS
之前都是记录点,这里是记录状态,因为之前是只能走周围的点,再远处的点就step++,但这里对于某种状态,整个地图都是1步能到达的,先俩for整体赋值vis和nextstates,开俩for遍历map每个点,对于每个点做翻转fliptile,翻转完如果找到了就停止剪枝(后面细说),翻转方法是,遍历本身+上下左右5个点做翻转,作为下一个状态,存入nextstates的realtime_map,同时也存入vis串,但还不能真正作为访问过的标记,翻转完出来再从0到count遍历vis,看你这个状态出现过没(我想用队列发现队列也没有直接判断元素有没有的功能,直接笨方法搞了。下一篇博客学到的map,这句话是AC完下一道题写写一篇博客的时候回来加的),如果没出现过,nextstates的该点的ans加1,压入队列,count++,去迎接下面的其他状态,如果没出现过,nextstates的realtimemap不用管,赋值就赋值了,也没压入队列,下一次值就覆盖了,ans也没加1,只有vis有变化,既然出现过那就赋值为空串吧,接着马上readyforfliptile,做一次翻转
再看下复杂度,对于这个数据,7次即可找到

第一次2*2四个方块翻转,执行4次,然后0111弹出做翻转,再翻转4个格子,但第一个vis过,直接从第二个格子开始翻转,看到翻转到最后一个格子就是答案了,上面提到的后面会说,这里来说,就是按照代码开始的思路是针对每一个states来把整个map地图做遍历翻转,这个例子恰巧是最后一个,用样例4 4例子好一些,意思就是那即使发现了0000也无动于衷等继续往下都翻转完,等q.front的时候,也会等弹到他再结束,这样就有很多无意义操作,实际测试发现也确实减少了,4 4 那个我每次压入列队就用个变量++,最后找到答案就输出发现从3k多次减少到1k多次,但这对复杂度来说是没效果的,复杂度是按照最差情况来
那对4*4样例来说

整个地图翻转一遍,翻到第5个点,这个点再出队从1~16再翻转,翻到第8个格子,第8个格子再从1~16翻转,总共最坏情况是(M*N)^(M*N)
为啥别人一句话的事,我要推好久才能反应过来复杂度是这个,哎
而且最开始也没有想清楚,一个点最多翻几次,以为可能会有答案是一个点翻不止一次的情况,现在发现每个点应该是最多翻转一次
妈的看题解去了。
我太傻逼了,发现洛谷题解 world_execute这人最开始排除的想到的最差才2^(M*N)
从每个点只有2种可能翻和不翻
我傻逼就傻逼,菜就菜,会变强的,这次我不会再放弃,——来自柳婼柳神建议PAT甲100才有资格做他徒弟。邝斌人一我十,人十我百。BUCT北化学长以为我是他们学校的,说当初的建议起了作用,很欣慰,我看了他的建议信才加的他好友。
这种博客真的好啊洛谷题解,逐步深入,这才是一个真正的好题解,很符合我之前博客里“感觉有必要把标题或者文章摘要写成“XXX没思路”的想法
但六年级小孩思维真的强,我看不明白,其他题解也没看懂,基本都是直接抄的博客。另外看到有点可爱的博客
我就不明白,题不是一个点么,为什么考虑整行,这块百度智能回答还可以但我也没理解
找到个差不多的题目
最后在这个文章这,循序渐进从一维翻转开始讲起,有点模糊的思路了,开始自己理解自己写
怎么都不理解,为什么把整行作为一个整体,回家一直想,管子都忘撸了,之前一天导好几管子,这几天刷题,都没功夫导了,再刷几天都不会导了。路上又想,终于想通了
这其实是一个结论,单独想结论肯定不行,从最差的复杂度2^(M*N)开始想
如果第一行都把所有情况枚举完了,翻或者不翻,2^N,那拿出其中一个情况,来看第二行怎么搞,之前的做法第二行每个点翻或者不翻,2种情况,但做一个剪枝优化,想一下第一行比如点(0,y),如果这个点是1,想变成0,必须第二行的点(1,y)翻转一下,而如果(0,y)是0,那(1,y)就不可以变动了,因为如果变动了,那第一行的(0,y)就会变成1,而第一行是枚举首行后,拿出其的中一种情况,分析此情况即第一行已经固定,(0,y+1)也是固定的,再没有哪个格子可以翻一下使得(0,y)再变成0的了。
所以后一行都根据前一行去看变还是不变,只需要枚举首行即可
这个题是之前西安艾教的V8讲过的,但去问V8不在,王茂广在沙发上给我讲的,最后被问住了,“我被基础初级班问住了”
有点像find the 倍数那个题
真佩服自己,现在的自己知道他之前写的是M*N)^(M*N)复杂度,写都不会写,但确实锻炼了代码和debug能力。
之前的方法是记录状态感觉太der太der了啊,想试试dfs却又不会写了,md深搜才是最难的啊比广搜难多了
想着N如果是3,000为都不翻,逐渐往后走,到最后一个的时候0/1为2种可能,000和001,return
然后在调用该dfs的下面写,作为return后的操作,即该位为1,再往后走0/1两种可能,010和011,return
然后在调用该dfs的下面写,作为return后的操作,即该位为1,再往后走循环上面操作,100/101/110/111
咋也写不出来,硬别一下午的代码
1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 using namespace std; 5 int M,N; 6 int map[15][15]; 7 int vis[15][15]; 8 9 int main() 10 { 11 while(cin>>M>>N){ 12 memset(vis,0,sizeof(vis)); 13 for(int i=0;i<M;i++) 14 for(int j=0;j<N;j++) 15 cin>>map[i][j]; 16 初始状态 17 dfs(0,0) 18 } 19 } 20 //艹枚举2^N怎么也想不出来 21 dfs(int x,int y) 22 { 23 if(vis[x][y]==0){ 24 for(int i=1;i<N;i++) 25 { 26 if(y+i<N){ 27 dfs(x,y+i); 28 状态变为1翻一下 29 dfs(此时状态) 30 31 } 32 if(y+i=N){//已经是最后一个 33 0赋值是一个状态 34 1赋值是一个状态 35 36 37 改完return; 38 39 } 40 41 } 42 } 43 }
哎,连最基本的123全排列深搜居然都不会了,感觉之前汉诺塔和谭浩强的这个都写不出来了
* *** ***** ***
*
之前无数次从0开始的ACM刷这个星星和汉诺塔
怎么枚举2^N个状态这卡住了,去看代码了,怎么都看不懂
一行一行执行结果都写纸上写满片子到一半放弃了,这种同理,看代码是看不懂的,还是理解思路自己写吧
学到个很有用的一招,我BFS/DFS琢磨半天,结果代码就是一句话的事(之前Find the 倍数比如3位,0、1、10、11、100、101、110、111只不过是省去了前导0,3位二进制可用2^3就是8,从0到十进制8遍历所有二进制去展示,20位十进制数是70位二进制数,2^70大概是20位十进制数,从0到十进制20位数遍历,去展示,可是10^9就基本超时了)
1 #include<cstdio> 2 #include<cstring> 3 #include <iostream> 4 #include <bitset> //输出二进制的头文件 5 using namespace std; 6 int main() 7 { 8 int a=9; 9 int b=0; 10 // cout<<((a>>0))<<endl; 11 // cout<<((a>>1))<<endl; 12 // cout<<((a>>2))<<endl; 13 14 // cout<<"1:"<<(1&1)<<endl; 15 // cout<<"2:"<<(2&1)<<endl; 16 // cout<<"3:"<<(3&1)<<endl; 17 // cout<<"4:"<<(4&1)<<endl; 18 // cout<<"5:"<<(5&1)<<endl; 19 // cout<<"6:"<<(6&1)<<endl; 20 // cout<<"7:"<<(7&1)<<endl; 21 // cout<<"8:"<<(8&1)<<endl; 22 cout<<" 右移0位"<<" 右移1位"<<" 右移2位"<<endl; 23 for(int i=0;i<8;i++) 24 cout <<"i:"<<i<<" "<<bitset<sizeof(i)*1>(i)<<" "<<bitset<sizeof(i)*1>(i>>1)<< " "<<bitset<sizeof(i)*1>(i>>2)<<endl; 25 // cout<<endl; 26 27 28 // cout<<"右移1位"<<endl; 29 //for(int i=0;i<8;i++) 30 // cout <<"i:"<<i<<" " << bitset<sizeof(i)*1>(i>>1) << endl; 31 //cout<<"右移2位"<<endl; 32 //for(int i=0;i<8;i++) 33 // cout <<"i:"<<i<<" " << bitset<sizeof(i)*1>(i>>2) << endl; 34 }

如果地图是N=3位的话, 那0~2^3这中间的所有二进制数,其实就是2^N个枚举的答案
引出来的左右移操作就是枚举状态的突破口,0~7最外层去枚举,而对于比如5来说,他的二进制0101就可以当作101,即第一个数翻转,第二个不翻转,第三个翻转
重点来了
0101(5)右移0位就是0101(5)
0101(5)右移1位就是0010(2)
0101(5)右移2位就是0001(1)
怎么表示这个5,0101,最朴素的是二进制对10取模。二进制除以10,再对10取模。除以100再对10取模,但把二进制数跟10来操作又有点der
他又不是一百零一
所以5不移位,再&1,就相当于取了最后一位1
5右移1位,0010这时候最后一位0,其实就是我想取的之前5的倒数第二位,再&1,就相当于取了这个0
5右移2位,0001这时候最后一位1,其实就是我想取的之前5的倒数第三位,再&1,就相当于取了这个1
状态怎么枚举知道了再去写代码
中途遇到几个思路上的问题,WA点:
1、循环0~2^N的时候,每次一个新状态,但此时fliptilemap已经被改过了,所以要重新再赋值一遍,见AC代码29~31行,我是一行一行调试发现再次进入遍历状态循环的时候,fliptilemap就不是想要的值
2、关于字典序,一开始想的是,涉及到字典序判断的时候,遍历每个值,如果当前的待比较的maybe_ans数组的值<ans已经存好最小字典序的值,就把标记变量f变为1,break,然后出来如果f==0就更新ans。但有个问题
如果输出应该是样例那个,但现在maybe_ans里是
0 0 0 1 1 0 1 0 1 1 0 0 1 1 1 0
就不行
再思考, if(maybe_ans[i][j]==0&&ans[i][j]==1) 也不行
反例
1 0 0 1 0 1 1 0 0 1 1 0 1 0 0 1
那如果maybe_ans[i][j]>ans[i][j],标志变1,break,出来如果表中是0就更新ans,也不行
反例:
0000 0000 0000 0001
理应更新,却没更新
再想,不应该单一判断,应该判断我比你现存的ans里的值小的时候,前面没有比你大的,如果前面比你大就搞个标记变量然后break,现在遇到比你小的再搞个标记变量然后break。
如果maybe_ans[i][j]>ans[i][j]就标志变为1,break
如果maybe_ans[i][j]<ans[i][j]就标志变为2,break
这样出来后,如果标志是2,就可以更新了
其实就是保证,我比你ans小的前面,没有比你ans大的,就更新ans
3、 发现states状态里,应该是用更新后的fliptilemap去赋值,我一直用map[i][j]赋值(我随便造了个1行5列的反例找到的这个问题)
1 5 0 1 1 0 1
其实是看了洛谷WA上的这句话,

害,感觉这种提示还是不太好,POJ/HDOJ那种单纯一个WA更加锻炼debug能力
4、改完一直92分,又看了洛谷讨论,比起字典序,步数最少才是应该输出的,步数一样再看字典序。(顺便回复了)————(AC后发现不用考虑字典序,后面说)
5、我没有犯但很多人犯的错误,想正确存储二进制,右移的时候,比如3位,先移2位才是取得的翻转状态第一位,而不是右移0位是第一位,右移0位是翻转状态的最后一位
我怎么改都92分
我的92分代码
1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 using namespace std; 5 int map[40][40]; 6 int fliptilemap[40][40]; 7 int maybe_ans[40][40];//作为中间存储变量,筛选后再进入ans 8 int states[40]; 9 void fliptile(); 10 int ans[40][40]; 11 int M,N; 12 int flag; 13 int main() 14 { 15 // freopen("zhishu.txt","r",stdin); 16 while(cin>>M>>N) 17 { 18 flag=0; 19 for(int i=0;i<M;i++) 20 for(int j=0;j<N;j++){ 21 cin>>map[i][j]; 22 fliptilemap[i][j]=map[i][j]; 23 } 24 for(int i=0;i<M;i++) 25 for(int j=0;j<N;j++) 26 ans[i][j]=1; 27 for(int i=0;i<(1<<N);i++){ 28 29 for(int a=0;a<M;a++)//一开始忘加了一直WA 30 for(int b=0;b<N;b++) 31 fliptilemap[a][b]=map[a][b]; 32 33 memset(maybe_ans,0,sizeof(maybe_ans)); 34 35 for(int j=0;j<N;j++){//自己写才发现,别说别人的代码,自己的代码一个月后都看不懂,重要是思路,然后自己写 36 states[j]=(i>>(N-j-1))&1;//至此fliptile里存的N位表示是否翻转 37 if(states[j]==1){ 38 fliptilemap[0][j]=(fliptilemap[0][j]+1)%2;//1翻 39 maybe_ans[0][j]++; 40 if(M>1) 41 fliptilemap[1][j]=(fliptilemap[1][j]+1)%2; 42 if(j!=0) 43 fliptilemap[0][j-1]=(fliptilemap[0][j-1]+1)%2; 44 if(j!=N-1) 45 fliptilemap[0][j+1]=(fliptilemap[0][j+1]+1)%2; 46 } 47 48 } 49 fliptile(); 50 } 51 if(flag==0) 52 cout<<"IMPOSSIBLE"<<endl; 53 else 54 for(int i=0;i<M;i++){ 55 for(int j=0;j<N;j++){ 56 if(j!=0) 57 cout<<" "<<ans[i][j]; 58 else 59 cout<<ans[i][j]; 60 } 61 cout<<endl; 62 } 63 64 } 65 } 66 void fliptile() 67 { 68 for(int i=1;i<M;i++) 69 { 70 for(int j=0;j<N;j++){ 71 if(fliptilemap[i-1][j]==1)//如果上一行的该点是1 72 { 73 fliptilemap[i][j]=(fliptilemap[i][j]+1)%2;//该点必须翻转 74 maybe_ans[i][j]++; 75 fliptilemap[i-1][j]=(fliptilemap[i-1][j]+1)%2;//该点翻转导致上一行该点也翻转 76 if(i+1<M) 77 fliptilemap[i+1][j]=(fliptilemap[i+1][j]+1)%2;//下一行没出界也得反转 78 if(j!=0) 79 fliptilemap[i][j-1]=(fliptilemap[i][j-1]+1)%2;//左边没出界也得翻转 80 if(j!=N-1) 81 fliptilemap[i][j+1]=(fliptilemap[i][j+1]+1)%2;//右边没出界限也得翻转 82 } 83 } 84 } 85 int can_not=0; 86 for(int i=0;i<N;i++)//全翻转完毕,如果最后一行不是全0,则不行 87 { 88 if(fliptilemap[M-1][i]==1){ 89 can_not=1; 90 break; 91 } 92 } 93 int maybe_num=0; 94 int ans_num=0; 95 int max_is_maybe_num=-1; 96 if(can_not==0){ 97 for(int i=0;i<M;i++) 98 for(int j=0;j<N;j++){ 99 if(ans[i][j]==1) 100 ans_num++; 101 if(maybe_ans[i][j]==1) 102 maybe_num++; 103 }//先看看是不是步骤最小,别直接看是不是字典序 104 105 if(maybe_num<ans_num) 106 max_is_maybe_num=0;//必须可以 107 108 if(maybe_num>ans_num) 109 max_is_maybe_num=1;//不可以 110 111 if(maybe_num=ans_num) 112 max_is_maybe_num=2;//还要再判断字典序 113 114 } 115 int p=0; 116 if(max_is_maybe_num==2) 117 { 118 flag=1; 119 for(int i=0;i<M&&p==0;i++) 120 for(int j=0;j<N;j++){ 121 if(maybe_ans[i][j]>ans[i][j]){ 122 p=1; 123 break; 124 } 125 if(maybe_ans[i][j]<ans[i][j]){ 126 p=2; 127 break; 128 } 129 } 130 } 131 if(p==1) 132 ; 133 if(p==2||max_is_maybe_num==0){ 134 135 for(int i=0;i<M;i++) 136 for(int j=0;j<N;j++) 137 ans[i][j]=maybe_ans[i][j]; 138 } 139 }
无奈对拍,对拍代码
1 //#include<bits/stdc++.h> 2 #include<stdio.h> 3 #include<iostream> 4 #include<time.h> 5 using namespace std; 6 char x[3]={'@','.','#'}; 7 char map[100][100]; 8 int main() { 9 srand(time(0) + (unsigned long long)(new char)); 10 11 int M = rand() % 15+1; 12 int N = rand() % 15+1; 13 14 cout<<M<<" "<<N<<endl; 15 for(int i=0;i<M;i++){ 16 for(int j=0;j<N;j++){ 17 int map = rand() % 2; 18 cout<<map<<" "; 19 } 20 cout<<endl; 21 } 22 23 }
随便搜了个CSDN的AC代码,在POJ和洛谷都是AC的,开始对拍,发现这组数据不一样
5 2 0 0 0 0 1 1 0 0 0 0
洛谷其他题解AC代码和我的代码都输出
0 1 1 1 0 1 1 1 0 1
但他妈的这CSDN这人的代码居然输出
1 0 1 1 1 0 1 1 1 0
什么JB玩意,艹!!!这都可以过(真想给俩平台发邮件加这组数据)(我AC之后去看他代码发现俩问题,1、方向数组里存的什么玩意,连右都没有。2、根本没写字典序啊,这咋AC的???????有啥我不知道的高深逻辑么??错错得正???OJ平台啥时候这么垃圾了)————(事后发现错误地方不是没字典序,这题不需要字典序,而是他犯了我上面说的5那个错误)
再找发现这个样例也不对,涉及字典序都不对,这哥们博客说的还挺在理的,(事后发现不用字典序,但测试数据里真的没有字典序数据)
1 5 1 1 1 1 1
现在只有可用平台是完美数据了吗?现在发现codeforce才是最吊的,那个hacker。(我AC后好奇测试了下,把自己AC代码改成去掉字典序的,POJ/洛谷都WA,但洛谷AC数据点是12/13,只有8没过,其实是其他小细节的错误)
艹不管了,再继续用洛谷大佬们的代码对拍
于是用洛谷这人的代码对拍
生成一个这个数据
1 13 0 0 0 1 0 1 0 1 1 1 0 0 1
妈的他的代码比我代码末尾多一个空格,啊这?????

但我代码加上空格也不对,且这人的代码POJ是WA的,因为有多余空格
继续对拍
15 2 1 1 1 0 0 1 1 1 1 1 1 0 1 0 1 1 1 1 0 1 0 1 0 0 0 0 1 1 1 0
这组数据我输出有15步,AC代码是13步
发现是字典序问题
妈的笔误,把步数判断双等号写成赋值了
还有就是
flag我只在 max_is_maybe_num为2的 时候赋值为1,忘记max_is_maybe_num为0的时候也就是步数更少的时候也要赋值为1,不然答案是对了但全输出IMPOSSIBLE
AC代码 (不信邪就想开15,依旧AC) 两个平台均可AC 今天不知咋的,POJ总提示网址出错要刷新好几遍才好

1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 using namespace std; 5 int map[15][15]; 6 int fliptilemap[15][15]; 7 int maybe_ans[15][15];//作为中间存储变量,筛选后再进入ans 8 int states[15]; 9 void fliptile(); 10 int ans[15][15]; 11 int M,N; 12 int flag; 13 int main() 14 { 15 // freopen("zhishu.txt","r",stdin); 16 while(cin>>M>>N) 17 { 18 flag=0; 19 for(int i=0;i<M;i++) 20 for(int j=0;j<N;j++){ 21 cin>>map[i][j]; 22 fliptilemap[i][j]=map[i][j]; 23 } 24 for(int i=0;i<M;i++) 25 for(int j=0;j<N;j++) 26 ans[i][j]=1; 27 for(int i=0;i<(1<<N);i++){ 28 29 for(int a=0;a<M;a++)//一开始忘加了一直WA 30 for(int b=0;b<N;b++) 31 fliptilemap[a][b]=map[a][b]; 32 33 memset(maybe_ans,0,sizeof(maybe_ans)); 34 35 for(int j=0;j<N;j++){//自己写才发现,别说别人的代码,自己的代码一个月后都看不懂,重要是思路,然后自己写 36 states[j]=(i>>(N-j-1))&1;//至此fliptile里存的N位表示是否翻转 37 if(states[j]==1){ 38 fliptilemap[0][j]=(fliptilemap[0][j]+1)%2;//1翻 39 maybe_ans[0][j]++; 40 if(M>1) 41 fliptilemap[1][j]=(fliptilemap[1][j]+1)%2; 42 if(j!=0) 43 fliptilemap[0][j-1]=(fliptilemap[0][j-1]+1)%2; 44 if(j!=N-1) 45 fliptilemap[0][j+1]=(fliptilemap[0][j+1]+1)%2; 46 } 47 48 } 49 fliptile(); 50 } 51 if(flag==0) 52 cout<<"IMPOSSIBLE"<<endl; 53 else 54 for(int i=0;i<M;i++){ 55 for(int j=0;j<N;j++){ 56 if(j!=0) 57 cout<<" "<<ans[i][j]; 58 else 59 cout<<ans[i][j]; 60 } 61 cout<<endl; 62 } 63 64 } 65 } 66 void fliptile() 67 { 68 for(int i=1;i<M;i++) 69 { 70 for(int j=0;j<N;j++){ 71 if(fliptilemap[i-1][j]==1)//如果上一行的该点是1 72 { 73 fliptilemap[i][j]=(fliptilemap[i][j]+1)%2;//该点必须翻转 74 maybe_ans[i][j]++; 75 fliptilemap[i-1][j]=(fliptilemap[i-1][j]+1)%2;//该点翻转导致上一行该点也翻转 76 if(i+1<M) 77 fliptilemap[i+1][j]=(fliptilemap[i+1][j]+1)%2;//下一行没出界也得反转 78 if(j!=0) 79 fliptilemap[i][j-1]=(fliptilemap[i][j-1]+1)%2;//左边没出界也得翻转 80 if(j!=N-1) 81 fliptilemap[i][j+1]=(fliptilemap[i][j+1]+1)%2;//右边没出界限也得翻转 82 } 83 } 84 } 85 int can_not=0; 86 for(int i=0;i<N;i++)//全翻转完毕,如果最后一行不是全0,则不行 87 { 88 if(fliptilemap[M-1][i]==1){ 89 can_not=1; 90 break; 91 } 92 } 93 int maybe_num=0; 94 int ans_num=0; 95 int max_is_maybe_num=-1; 96 if(can_not==0){ 97 for(int i=0;i<M;i++) 98 for(int j=0;j<N;j++){ 99 if(ans[i][j]==1) 100 ans_num++; 101 if(maybe_ans[i][j]==1) 102 maybe_num++; 103 }//先看看是不是步骤最小,别直接看是不是字典序 104 105 if(maybe_num<ans_num) 106 max_is_maybe_num=0;//必须可以 107 108 if(maybe_num>ans_num) 109 max_is_maybe_num=1;//不可以 110 111 if(maybe_num==ans_num) 112 max_is_maybe_num=2;//还要再判断字典序 113 114 } 115 int p=0; 116 if(max_is_maybe_num==0) 117 flag=1; 118 if(max_is_maybe_num==2) 119 { 120 flag=1; 121 for(int i=0;i<M&&p==0;i++) 122 for(int j=0;j<N;j++){ 123 if(maybe_ans[i][j]>ans[i][j]){ 124 p=1; 125 break; 126 } 127 if(maybe_ans[i][j]<ans[i][j]){ 128 p=2; 129 break; 130 } 131 } 132 } 133 // cout<<"!"<<flag<<endl; 134 if(p==1) 135 ; 136 if(p==2||max_is_maybe_num==0){ 137 138 for(int i=0;i<M;i++) 139 for(int j=0;j<N;j++) 140 ans[i][j]=maybe_ans[i][j]; 141 } 142 }
AC后查字典序的其他写法发现大佬博客里的一句话,真的直接点睛传神了,
“二进制枚举第一行还有一个好处就是它本身就是按照字典序大小枚举的,完美符合题目要求”
去掉字典序发现我代码WA了,原因在于我初始值ans都是1
1 1 1
这个例子就过不了,我AC代码能过是巧合,2^1是2,0/1俩个状态,1的时候,ans和我目前的maybe_ans都是1,相等。那么就误进入了字典序比较这里,经过121那几行字典序处理弹出来时候,p是0,max_is_maybe_num是2,根本没进入到138、139、140那里,而正确输出1是因为max_is_maybe_num是2进入后flag是1了,返回主函数main那,flag为1输出的ans还是初始赋的1,虽然对但代码逻辑大大滴漏洞
想把把ans都初始为2,但也不行,为2最后在102行判断也只是加1,依旧是相等
又想了下,那累加呢,正好第一次可以把正确答案更新给ans,之后有更优的也可以更新,最后洛谷和POJ两个平台都可以AC
AC代码:
1 //#include<stdio.h> 2 //#include<iostream> 3 //#include<string.h> 4 //using namespace std; 5 //int map[15][15]; 6 //int fliptilemap[15][15]; 7 //int maybe_ans[15][15];//作为中间存储变量,筛选后再进入ans 8 //int states[15]; 9 //void fliptile(); 10 //int ans[15][15]; 11 //int M,N; 12 //int flag; 13 //int main() 14 //{ 15 // freopen("zhishu.txt","r",stdin); 16 // while(cin>>M>>N) 17 // { 18 // flag=0; 19 // for(int i=0;i<M;i++) 20 // for(int j=0;j<N;j++){ 21 // cin>>map[i][j]; 22 // fliptilemap[i][j]=map[i][j]; 23 // } 24 // for(int i=0;i<M;i++) 25 // for(int j=0;j<N;j++) 26 // ans[i][j]=1; 27 //// for(int i=0;i<(1<<N);i++){ 28 //int i=1; 29 // for(int a=0;a<M;a++)//一开始忘加了一直WA 30 // for(int b=0;b<N;b++) 31 // fliptilemap[a][b]=map[a][b]; 32 // 33 // memset(maybe_ans,0,sizeof(maybe_ans)); 34 // 35 // for(int j=0;j<N;j++){//自己写才发现,别说别人的代码,自己的代码一个月后都看不懂,重要是思路,然后自己写 36 // states[j]=(i>>(N-j-1))&1;//至此fliptile里存的N位表示是否翻转 37 // if(states[j]==1){ 38 // fliptilemap[0][j]=(fliptilemap[0][j]+1)%2;//1翻 39 // maybe_ans[0][j]++; 40 // if(M>1) 41 // fliptilemap[1][j]=(fliptilemap[1][j]+1)%2; 42 // if(j!=0) 43 // fliptilemap[0][j-1]=(fliptilemap[0][j-1]+1)%2; 44 // if(j!=N-1) 45 // fliptilemap[0][j+1]=(fliptilemap[0][j+1]+1)%2; 46 // } 47 // 48 // } 49 // cout<<fliptilemap[0][0]<<endl; 50 // fliptile(); 51 //// } 52 // if(flag==0) 53 // cout<<"IMPOSSIBLE"<<endl; 54 // else 55 // for(int i=0;i<M;i++){ 56 // for(int j=0;j<N;j++){ 57 // if(j!=0) 58 // cout<<" "<<ans[i][j]; 59 // else 60 // cout<<ans[i][j]; 61 // } 62 // cout<<endl; 63 // } 64 // 65 // } 66 //} 67 //void fliptile() 68 //{ 69 // for(int i=1;i<M;i++) 70 // { 71 // for(int j=0;j<N;j++){ 72 // if(fliptilemap[i-1][j]==1)//如果上一行的该点是1 73 // { 74 // fliptilemap[i][j]=(fliptilemap[i][j]+1)%2;//该点必须翻转 75 // maybe_ans[i][j]++; 76 // fliptilemap[i-1][j]=(fliptilemap[i-1][j]+1)%2;//该点翻转导致上一行该点也翻转 77 // if(i+1<M) 78 // fliptilemap[i+1][j]=(fliptilemap[i+1][j]+1)%2;//下一行没出界也得反转 79 // if(j!=0) 80 // fliptilemap[i][j-1]=(fliptilemap[i][j-1]+1)%2;//左边没出界也得翻转 81 // if(j!=N-1) 82 // fliptilemap[i][j+1]=(fliptilemap[i][j+1]+1)%2;//右边没出界限也得翻转 83 // } 84 // } 85 // } 86 // int can_not=0; 87 // for(int i=0;i<N;i++)//全翻转完毕,如果最后一行不是全0,则不行 88 // { 89 // if(fliptilemap[M-1][i]==1){ 90 // can_not=1; 91 // break; 92 // } 93 // } 94 // int maybe_num=0; 95 // int ans_num=0; 96 // int max_is_maybe_num=-1; 97 // if(can_not==0){ 98 // for(int i=0;i<M;i++) 99 // for(int j=0;j<N;j++){ 100 // if(ans[i][j]==1) 101 // ans_num++; 102 // if(maybe_ans[i][j]==1) 103 // maybe_num++; 104 // }//先看看是不是步骤最小,别直接看是不是字典序 105 // 106 //cout<<maybe_num<<" "<<ans_num<<"@"<<endl; 107 // if(maybe_num<ans_num){ 108 // cout<<"!"<<endl; 109 // flag=1; 110 // for(int i=0;i<M;i++) 111 // for(int j=0;j<N;j++) 112 // ans[i][j]=maybe_ans[i][j]; 113 // } 114 // 115 // } 116 // 117 //} 118 119 #include<stdio.h> 120 #include<iostream> 121 #include<string.h> 122 using namespace std; 123 int map[15][15]; 124 int fliptilemap[15][15]; 125 int maybe_ans[15][15];//作为中间存储变量,筛选后再进入ans 126 int states[15]; 127 void fliptile(); 128 int ans[15][15]; 129 int M,N; 130 int flag; 131 int main() 132 { 133 // freopen("zhishu.txt","r",stdin); 134 while(cin>>M>>N) 135 { 136 flag=0; 137 for(int i=0;i<M;i++) 138 for(int j=0;j<N;j++){ 139 cin>>map[i][j]; 140 fliptilemap[i][j]=map[i][j]; 141 } 142 for(int i=0;i<M;i++) 143 for(int j=0;j<N;j++) 144 ans[i][j]=2; 145 for(int i=0;i<(1<<N);i++){ 146 147 for(int a=0;a<M;a++)//一开始忘加了一直WA 148 for(int b=0;b<N;b++) 149 fliptilemap[a][b]=map[a][b]; 150 151 memset(maybe_ans,0,sizeof(maybe_ans)); 152 153 for(int j=0;j<N;j++){//自己写才发现,别说别人的代码,自己的代码一个月后都看不懂,重要是思路,然后自己写 154 states[j]=(i>>(N-j-1))&1;//至此fliptile里存的N位表示是否翻转 155 if(states[j]==1){ 156 fliptilemap[0][j]=(fliptilemap[0][j]+1)%2;//1翻 157 maybe_ans[0][j]++; 158 if(M>1) 159 fliptilemap[1][j]=(fliptilemap[1][j]+1)%2; 160 if(j!=0) 161 fliptilemap[0][j-1]=(fliptilemap[0][j-1]+1)%2; 162 if(j!=N-1) 163 fliptilemap[0][j+1]=(fliptilemap[0][j+1]+1)%2; 164 } 165 166 } 167 fliptile(); 168 } 169 if(flag==0) 170 cout<<"IMPOSSIBLE"<<endl; 171 else 172 for(int i=0;i<M;i++){ 173 for(int j=0;j<N;j++){ 174 if(j!=0) 175 cout<<" "<<ans[i][j]; 176 else 177 cout<<ans[i][j]; 178 } 179 cout<<endl; 180 } 181 182 } 183 } 184 void fliptile() 185 { 186 for(int i=1;i<M;i++) 187 { 188 for(int j=0;j<N;j++){ 189 if(fliptilemap[i-1][j]==1)//如果上一行的该点是1 190 { 191 fliptilemap[i][j]=(fliptilemap[i][j]+1)%2;//该点必须翻转 192 maybe_ans[i][j]++; 193 fliptilemap[i-1][j]=(fliptilemap[i-1][j]+1)%2;//该点翻转导致上一行该点也翻转 194 if(i+1<M) 195 fliptilemap[i+1][j]=(fliptilemap[i+1][j]+1)%2;//下一行没出界也得反转 196 if(j!=0) 197 fliptilemap[i][j-1]=(fliptilemap[i][j-1]+1)%2;//左边没出界也得翻转 198 if(j!=N-1) 199 fliptilemap[i][j+1]=(fliptilemap[i][j+1]+1)%2;//右边没出界限也得翻转 200 } 201 } 202 } 203 int can_not=0; 204 for(int i=0;i<N;i++)//全翻转完毕,如果最后一行不是全0,则不行 205 { 206 if(fliptilemap[M-1][i]==1){ 207 can_not=1; 208 break; 209 } 210 } 211 int maybe_num=0; 212 int ans_num=0; 213 int max_is_maybe_num=-1; 214 if(can_not==0){ 215 for(int i=0;i<M;i++) 216 for(int j=0;j<N;j++){ 217 ans_num=ans_num+ans[i][j]; 218 maybe_num=maybe_num+maybe_ans[i][j]; 219 } 220 // cout<<"!"<<maybe_num<<" "<<ans_num<<endl; 221 if(maybe_num<ans_num){ 222 223 flag=1; 224 for(int i=0;i<M;i++) 225 for(int j=0;j<N;j++) 226 ans[i][j]=maybe_ans[i][j]; 227 228 } 229 230 } 231 232 }
思考小于等于行不行,肯定不行啊,等于的话一定不是最小字典序
------------------------------分割线-----------------------------------
期间用到什么现用现查的小知识和踩的坑(这题应该是重新开始刷题后遇到最难的题了,打算AC后做个视频讲解)
###:比较两个字符串或者是string是否相等函数、string拼接
###:int<=>char<=>string 互相转换 这些家伙为啥总用黑色,没白色看着舒服
###:string相关:
string二维数组
string数组初始化
string的替换某位, var.replace (3,3, dest);
string获取指定某位, string str2_s = str2.substr(0, np3);
string转化为int, num = std::stoi(str); ,字母前终止,只能把转化数字,也就是说string里必须存的是数字
###:来回把代码封装成函数的时候{}大括号别弄丢一个
###:复杂度后面再弄吧
###:谁能告诉我这傻逼悬浮提示函数功能怎么关掉,找了一堆都不好使
###:智障玩意,声明定义无意义的狗屁,拘泥于名词还写个屁代码,就跟文科和傻逼测试似得,类似于进屋躺床上需要及步,md毫无意义先搁置,放几个博客、博客、博客
###:写代码开着任务管理器防止chrome页面过多总关闭,防止超内存给我干蓝屏
###:之前不理解string含义,以为应该vis[225][225]才是225个串,其实vis[255]就已经是255个很长很长的string串了
###:string不可以用memset,涉及内存泄漏什么的暂时不懂,先用{}空串就行了
###:位运算
###:左移右移 左移x位相当于乘2^x次方
###:C+++二进制输出
###:运算符就是操作符
###:cout重载了运算符<<,用移位运算符的时候记得要加括号,具体先搁置
###:傻逼百度AI回答:只有搜这个关键词才能搜到,之前有印象,这次用到全部初始化为1看看有什么好方法,特意找到这个回答,结果发现3宏定义方法编译器报错,之前回答过memset为1的也是,不对的玩意瞎误人子弟!

###:POJ数据太水,洛谷很不错,但洛谷每次提交有分数显示,比如50分下次90分,就知道改的方向正确了,而POJ和比赛没有这些,就WA或者AC
虽然现在找工作时间紧迫,但也还是别用这种分数的提示吧。虽然不知道是不是徒劳折腾自己
###:之前知乎说考研黑虎掏心力道更强,三年后知乎都是不要考研的。如今刷题也感觉POJ?HDOJ这些没落了,好像没啥人刷,整天臭鱼烂虾吴师兄和公子龙公众号有算法,不知道是不是逃避leetcode的缘故,算了,不会再放弃,像岛娘说的,不管怎么样要把算法竞赛学好。刘汝佳:不要总临阵磨枪了。
###:洛谷这人说要开到40,但我15是可以的,顺便回复了他
###;看再多题解也没用,简单看几个然后自己想,才能想通。还有就是别人的代码看不懂,没必要看,q神都不看别人代码,主要是思路
###:这题跟搜索没关系,纯枚举


浙公网安备 33010602011771号