链接:https://ac.nowcoder.com/acm/contest/4462/C
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
一列上有12个孔,这12个孔中有些孔被遮挡住了。
假定我们用 '-' 来表示没被遮挡住的孔,用 'o' 来表示被遮挡住的孔。
如果相邻的三个孔有两个孔被遮挡,并且被遮挡的两个孔相邻,就是 '-oo' 和 'oo-'。
对于这样的三个孔,我们可以将中间的孔的遮挡物移开,代价是将一端的遮挡物移到另一端没有被遮挡的孔上面。
对于一列给定的孔,你的任务是制定操作的顺序,使得最后剩余的被遮挡的孔的个数最少,并输出最后剩余的被遮挡的孔的个数。
输入描述:
第一行输入一个n,n≤105。
接下来n行,每行12个字符,代表孔的状态。
输出描述:
对于每行输入在一行中输出一个数字代表答案。
输入
5 ---oo------- -o--o-oo---- -o----ooo--- oooooooooooo oooooooooo-o
输出
1 2 3 12 1
DFS即可,可以将这个字符串看作是一个二进制串,把所有的情况先处理一下,然后对应询问直接输出即可。
直接按题意DFS,感觉也能写
1 #include <bits/stdc++.h> 2 typedef long long LL; 3 const int INF=0x3f3f3f3f; 4 const double eps =1e-8; 5 const int mod=1e9+7; 6 const int maxn=1e5+10; 7 using namespace std; 8 9 string str; 10 int ans; 11 12 int DFS() 13 { 14 for(int i=0;i<10;i++) 15 { 16 if(str[i]=='-'&&str[i+1]=='o'&&str[i+2]=='o') 17 { 18 str[i]='o'; str[i+1]='-'; str[i+2]='-'; 19 DFS(); 20 str[i]='-'; str[i+1]='o'; str[i+2]='o'; 21 } 22 if(str[i]=='o'&&str[i+1]=='o'&&str[i+2]=='-') 23 { 24 str[i]='-'; str[i+1]='-'; str[i+2]='o'; 25 DFS(); 26 str[i]='o'; str[i+1]='o'; str[i+2]='-'; 27 } 28 } 29 int num=0; 30 for(int i=0;i<12;i++) 31 { 32 if(str[i]=='o') num++; 33 } 34 ans=min(ans,num); 35 } 36 37 int main() 38 { 39 #ifdef DEBUG 40 freopen("sample.txt","r",stdin); 41 #endif 42 43 int T; 44 cin>>T; 45 while(T--) 46 { 47 ans=INF; 48 cin>>str; 49 DFS(); 50 cout<<ans<<' '; 51 } 52 53 return 0; 54 }
状态压缩
原牛客题解:https://ac.nowcoder.com/acm/problem/blogs/202476
一共12个孔,只有2^12=4096种情况,但是查询次数很多,我们可以考虑用二进制1和0表示‘o’和‘-’两种状态,进行状态压缩,用一个二进制数代表一种情况。然后再在记忆化搜索的过程中把已知情况的答案记录下来,方便下次直接用。
搜索的时候就找 '-oo' 和 'oo-',注意到它们有共同点:中间是1,两边不相同。“将中间的孔的遮挡物移开,将一端的遮挡物移到另一端没有被遮挡的孔上面。”其实就是把中间的1变为0,再把两边互换,对于二进制数011和110,我们只要将它^111即异或7(1和0异或上0不变,异或1则可互换)
附:运算符优先级
1 #include <bits/stdc++.h> 2 typedef long long LL; 3 const int INF=0x3f3f3f3f; 4 const double eps =1e-8; 5 const int mod=1e9+7; 6 const int maxn=1e5+10; 7 using namespace std; 8 9 int vis[1<<12]; //记忆化数组 10 11 int DFS(int x) 12 { 13 if(vis[x] || x==0) return vis[x]; //如果这种状态已经搜过了直接返回答案 14 int num=0; //状态中1的个数 15 for(int i=0;i<12;i++) 16 if(x>>i&1) num++; 17 for(int i=0;i<10;i++) //枚举'-oo' 和 'oo-'的右端 18 { 19 if(x>>(i+1)&1 && x>>i&1 ^ x>>(i+2)&1) //如果中间是1,两边不相同(异或为1)。注意运算符优先级,这里由高到低分别是()、+、>>、&、^ 20 num=min(num,DFS(x^7<<i)); //进行操作继续搜索 21 } 22 return vis[x]=num; //记忆化 23 } 24 25 int main() 26 { 27 #ifdef DEBUG 28 freopen("sample.txt","r",stdin); 29 #endif 30 31 int T; 32 scanf("%d",&T); 33 while(T--) 34 { 35 int x=0; vis[0]=0; 36 char str[15]; 37 scanf("%s",str); 38 for(int i=0;i<12;i++) 39 x=(x<<1)+(str[i]=='o'? 1:0); //将状态压缩进二进制数x 40 printf("%d ",DFS(x)); 41 } 42 43 return 0; 44 }
-