常用技巧:反转/开关问题
这类型题目的特点是,要反转一个点的状态,会顺带反转周围一个范围内的状态,或者说是只能对一个范围进行反转。
针对这类题目,要把握的有两点:
1)每个区间最多也就是被反转一次,两次及其以上是没有意义的
2)分析每个点受几个区间影响或者是对几个区间产生影响,对当前点考虑反转时,优先满足已经走过的点(上一层或上一个)的可行性,而不必考虑自己,自己的问题留给下一个和自己有关的点考虑。当这个过程结束后,看看最后一批点满不满足条件,就可以知道当前情况可不可行以及当前可能的最小解。
例题1,POJ3276,是在一行上操作,要找出最小操作数以及最小操作数时的操作范围
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
int n,d[5005],f[5005],ans1,ans2;
int cal(int k){
memset(f,0,sizeof(f));
int sum=0,res=0;
for(int i=0;i+k-1<n;i++){
if((d[i]+sum)%2!=0){
f[i]=1;
res++;
}
sum+=f[i];
if(i-k+1>=0){
sum-=f[i-k+1];
}
}
for(int i=n-k+1;i<n;i++){
if((d[i]+sum)%2!=0) return -1;
if(i-k+1>=0){
sum-=f[i-k+1];
}
}
return res;
}
int main(){
scanf("%d",&n);
char c;
for(int i=0;i<n;i++){
cin>>c;
if(c=='B') d[i]=1;
else if(c=='F') d[i]=0;
}
ans2=0x7fffffff;
for(int i=1;i<=n;i++){
int m=cal(i);
if(m>=0&&m<ans2){
ans1=i;
ans2=m;
}
}
printf("%d %d\n",ans1,ans2);
return 0;
}
例题2,POJ3279,在二维数组里操作
#include<stdio.h>
#include<string.h>
int m,n,maze[20][20],flip[20][20],res[20][20],go[5][2]={0,0,1,0,0,-1,-1,0,0,1};
int getcol(int x,int y){
int cnt=maze[x][y];
for(int i=0;i<5;i++){
int nx=x+go[i][0],ny=y+go[i][1];
if(nx<0||ny<0||nx>=m||ny>=n) continue;
cnt+=flip[nx][ny];
}
return cnt%2;
}
int cal(){
for(int i=1;i<m;i++){
for(int j=0;j<n;j++){
if(getcol(i-1,j)==1) flip[i][j]=1;
}
}
for(int i=0;i<n;i++){
if(getcol(m-1,i)==1) return -1;
}
int cnt=0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
cnt+=flip[i][j];
}
}
return cnt;
}
int main(){
scanf("%d%d",&m,&n);
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
scanf("%d",&maze[i][j]);
}
}
int maxstate=(1<<n)-1,ans=0x7fffffff;
for(int i=0;i<=maxstate;i++){
memset(flip,0,sizeof(flip));
for(int j=0;j<n;j++) flip[0][n-1-j]=(i>>j)&1;
int tmp=cal();
if(tmp>=0&&tmp<ans){
ans=tmp;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
res[i][j]=flip[i][j];
}
}
}
}
if(ans==0x7fffffff) printf("IMPOSSIBLE\n");
else{
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(j!=0) printf(" ");
printf("%d",res[i][j]);
}
printf("\n");
}
}
return 0;
}


浙公网安备 33010602011771号