这题就是上一节的那题的扩展版了
首先第一行的状态如果确定了 那么后面要进行的操作也是确定的 所以枚举一下第一行的状态就好了
但是不同的是 上一题的第一个状态是确定的 但是这题的第一个状态不是确定的 因为可以通过按很多不同的按钮使得第一个的状态改变 而上一题只能有一个操作使得第一个状态的改变 所以这个还需要加一个二进制枚举一下第一行的状态 然后操作后面的按钮使得第一行全部变为符合要求的状态 再判断一下合法解
#include<bits/stdc++.h>
using namespace std;
const int N=6;
char str[N][N],temp[N][N];
int dx[6]={-1,0,1,0,0},dy[6]={0,1,0,-1,0};//上 右 下 左 中
void trans(int x,int y)//用来改变一个位置
{
for(int i=0;i<5;i++)//枚举五个方向
{
int tx=x+dx[i],ty=y+dy[i];
if(tx<0||tx>4||ty<0||ty>4) continue;
temp[tx][ty]^=1;
}
}
void solve()
{
for(int i=0;i<5;i++) scanf("%s",str[i]);
int ans=7;
for(int i=0;i<32;i++)
{
int cnt=0;
memcpy(temp,str,sizeof str);
for(int j=0;j<5;j++)//枚举改第一行的状态
if(i>>j&1) cnt++,trans(0,j);
for(int j=0;j<4;j++)//枚举改剩下的几行
for(int k=0;k<5;k++)
if(temp[j][k]=='0') cnt++,trans(j+1,k);
for(int j=0;j<5;j++)//看看最后几行是不是全为1
if(temp[4][j]=='0') cnt=0x3f3f3f3f;
ans=min(cnt,ans);
}
if(ans<=6) printf("%d\n",ans);//假如小于6了
else puts("-1");
}
int main()
{
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}
朴素思路:
1.首先用全排列枚举1到9的所有方案
2.枚举a b c的位数 (n=a+b/c)
3.判断合不合法
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 10;
int cnt=0;
int num[9]={1,2,3,4,5,6,7,8,9};
int cal(int i,int j)
{
int res=0;
for(int k = i+1 ; k<=j ; k++)
{
res=res*10+num[k];
}
return res;
}
// 设target = a+b/c,
int main()
{
int target;
cin>>target;
// 枚举1~9的所有情况
do
{
for(int i = 0 ; i<9 ; i++) // 二重循环枚举组合
{
for(int j = i+1 ; j<9 ; j++)
{
int a = cal(-1,i);
int b = cal(i,j);
int c = cal(j,8);
if(target*c==a*c+b)cnt++; // 注意,由于c++是整除运算,因此我们用乘法来替代除法
}
}
}while(next_permutation(num,num+9));
cout<<cnt<<endl;
return 0;
}
优化做法:
首先n的在上面的做法是没用过的 因为是在枚举a b c 那么是不是就可以枚举 a b (n=a+b/c) => ( b=n*c - a*c ) 然后通过这个式子就可以直接算出 b 了 就不用枚举了 那么我们在dfs a 选什么的时候 再 dfs c 选什么 再判断一下b合不合法就行了
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 10;
int n;
bool st[N], backup[N];
int ans;
bool check(int a, int c)
{
long long b = n * (long long)c - a * c;//这个可能会溢出
if (!a || !b || !c) return false;
memcpy(backup, st, sizeof st);
while (b)
{
int x = b % 10; // 取个位
b /= 10; // 个位删掉
if (!x || backup[x]) return false;
backup[x] = true;
}
for (int i = 1; i <= 9; i ++ )
if (!backup[i])
return false;
return true;
}
void dfs_c(int u, int a, int c)
{
if (u > 9) return;
if (check(a, c)) ans ++ ;
for (int i = 1; i <= 9; i ++ )
if (!st[i])
{
st[i] = true;
dfs_c(u + 1, a, c * 10 + i);
st[i] = false;
}
}
void dfs_a(int u, int a)
{
if (a >= n) return; //剪枝 a如果>=n 一定不是合法的
if (a) dfs_c(u, a, 0);//如果a不为0是才去枚举c 一种剪枝
for (int i = 1; i <= 9; i ++ )
if (!st[i])
{
st[i] = true;
dfs_a(u + 1, a * 10 + i);
st[i] = false;
}
}
int main()
{
cin >> n;
dfs_a(0, 0);//选了0个数 a的取值为0
cout << ans << endl;
return 0;
}
这题又和费解的开关是同一类题 但是这题没有费解的开关那种性质了 按第一行并不能确定所有状态 所以我们直接二进制暴力枚举4*4的棋盘怎么按 就行了
#include <bits/stdc++.h>
using namespace std;
char g[7][7];
char backup[7][7];
int get(int a,int b)
{
return 4*a+b;
}
void turn(int a,int b)
{
if(g[a][b]=='+') g[a][b]='-';
else g[a][b]='+';
}
int main()
{
for(int i=0;i<4;i++)
cin>>g[i];
vector<pair<int,int>>res;
for(int i=0;i<1<<16;i++)
{
vector<pair<int,int>> t;
memcpy(backup,g,sizeof g);
for(int j=0;j<4;j++)
for(int k=0;k<4;k++)
{
if(i>>get(j,k)&1)
{
t.push_back({j,k});
for(int l=0;l<4;l++)
turn(j,l),turn(l,k);
turn(j,k);
}
}
int fl=false;
for(int j=0;j<4;j++)
for(int k=0;k<4;k++)
if(g[j][k]=='+')
fl=true;
if(!fl&&(!res.size()||res.size()>t.size()))
res=t;
memcpy(g,backup,sizeof backup);
}
cout<<res.size()<<endl;
for(int i=0;i<res.size();i++)
{
cout<<res[i].first+1<<" "<<res[i].second+1<<endl;
}
return 0;
}
9.数的范围
二分模板题
我的总结 二分左右边界的时候不要想着模板 我个人觉得是真的乱
二分的时候
首先要自己自己二分的是左边界还是右边界
然后再去将没有等号的判断条件放到你不需要的区间那里
比如下面这段代码 我需要的是右边界 所以是左区间 那么我就把+1 -1 扔到这里右区间
while(l<r)
{
int mid=l+r>>1;
if(a[mid]>=x) r=mid;
else l=mid+1;
}
另外一个同理
while(l<r)
{
int mid=l+r+1>>1;
if(a[mid]>x) r=mid-1;
else l=mid;
}
代码实现
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int main()
{
int n,q;
scanf("%d%d",&n,&q);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
while(q--)
{
int x;
scanf("%d",&x);
int l=0,r=n-1;
while(l<r)
{
int mid=l+r>>1;
if(a[mid]>=x) r=mid;
else l=mid+1;
}
if(a[l]==x)
{
printf("%d ",l);
l=0,r=n-1;
while(l<r)
{
int mid=l+r+1>>1;
if(a[mid]>x) r=mid-1;
else l=mid;
}
printf("%d\n",l);
}
else printf("-1 -1\n");
}
return 0;
}
10.数的三次根
这题也是二分逼近答案就好了
浮点二分还比较简单 不用考虑边界问题
#include <bits/stdc++.h>
using namespace std;
const double eps=1e-8;
int main()
{
double n;
scanf("%lf",&n);
double l=-10000,r=10000;
while(r-l>eps)
{
double mid=(l+r)/2;
if(mid*mid*mid>=n) r=mid;
else l=mid;
}
printf("%lf\n",l);
return 0;
}