92. 递归实现指数型枚举 - AcWing题库
#include <bits/stdc++.h>
using namespace std;
const int N=17;
int n;
bool vis[N];//记录某一个数是否出现过
void dfs(int dep){// if(vis[dep])continue;//没有这一句 因为一定不会有已经选过的数if(dep==n+1){for(int i=1;i<=n;i++){if(vis[i])cout<<i<<' ';}cout<<'\n';return ;//记得要return}vis[dep]=1;//选上dfs(dep+1);//去判断下一个数vis[dep]=0;//不选dfs(dep+1);//去判断下一个数
}
int main(){cin>>n;dfs(1);return 0;
}
//输入3 输出 1 12 123 2 23 3 特点:长度不固定 不会往前找
94. 递归实现排列型枚举 - AcWing题库
#include <bits/stdc++.h>
using namespace std;
const int N=17;
int n;
bool vis[N]={};//记录某一个数是否出现过
vector<int>ans;
void dfs(int dep){//这里的dep表示的是够多少个数if(dep==n+1){for(auto i:ans)cout<<i<<' ';cout <<'\n';return ;}for(int i=st;i<=n;i++){if(vis[i])continue;vis[i]=1;ans.push_back(i);dfs(dep+1);//这里要是搜索下一个位置 一定要是dep+1vis[i]=0;ans.pop_back();}
}//这样的意思是从第一个数开始搜 答案一定要够n个数才能输出
int main(){cin>>n;dfs(1);return 0;
}
//输入3 输出123 132 213 231 321 312 特点长度固定,可以往前找
93. 递归实现组合型枚举 - AcWing题库
#include <bits/stdc++.h>
using namespace std;
const int N=100;
int a[N];
int n,m;//n个数中选择m个
int vis[N];
void dfs(int dep,int st){//st数组是为了保证顺序每次只往后面找if(dep==m+1){//选够m个数了 该选第m+1个数了for(int i=1;i<=n;i++){if(vis[i]==1)cout<<a[i]<<" ";}cout<<'\n';return ;}for(int i=st;i<=n;i++){if(!vis[i]){vis[i]=1;dfs(dep+1,i+1);//这里是i+1 因为第i个数字做出判断之后下次要判断的应该是它后面的数vis[i]=0;}}
}
int main(){cin>>n>>m;for(int i=1;i<=n;i++)a[i]=i;dfs(1,1);return 0;
}
输入:5 3
输出 123 124 125 134 135 145 234 235 245 345 特点长度固定,不会往前找
dfs与for循环结合 长度固定 可以往前找 (可以用st限制)
dfs不与for循环结合 长度不固定 不能往前找
95. 费解的开关 - AcWing题库
#include <bits/stdc++.h>
using namespace std;
const int N = 6;
int xx[N] = {-1, 0, 1, 0, 0};//上 右 下 左 中
int yy[N] = {0, 1, 0, -1, 0};
char g[N][N], backup[N][N];// 这个操作是把(x, y)以及上下左右的灯都变成相反的颜色
void turn (int x, int y){for (int i = 0; i < 5; i ++ ){int a = x + xx[i], b = y + yy[i];//如果在边界外边,直接忽略即可if (a < 0 || a >= 5 || b < 0 || b >= 5) continue;g[a][b] ^= 1; //异或,不同的时候就变成相反的数//同0异1}
}int main(){int n;scanf("%d", &n);while(n -- ){// 按行输入,把每一行当成一个字符串for (int i = 0; i < 5; i ++ ) cin >> g[i];int res = 0x3f3f;// 这里我们枚举了第一行的32种按法,不用管是亮是灭,把第一行所有情况都按一遍// 按每种情况的第一行,去遍历接下来的行// 枚举32种第一行的按法只是可能会减少步数,如果直接从第二行开始答案一定是固定的了,找不到最优解或者可能没有解//枚举第一行的意义是:不需要在意第一行的灯是灭是暗,只需把第一行的按法枚举一遍,也就是我们说的 “操作”,
//每个位置都有两种选择,按(用1表示)或者不按(用0表示),遍历这32种操作引发的情况,
//每一次再通过res = min(res, step);把最小步数存一下,就能找到最优解for (int op = 0; op < 32; op ++ ){//枚举第一行每一种按法 //好好想想这里为什么要是<32:11111是31
//是说我们输入的已知的是第一行灯亮或暗的状态,而我们枚举的32种是我们对灯的操作,按还是不按。// 我在对这种情况操作的时候,得先备用一下// 把原始数组备份一下,然后操作g,操作完了还原,然后再操作memcpy(backup, g, sizeof g);//用于复制数组int step = 0;// 第一行的按法(在这里 1 表示按了, 0 表示不按),这里只是为了输出第一行按完之后的状态for (int i = 0; i < 5; i ++ )if (op >> i & 1) // 数字2 对应了 00010 表示第2个位置的按一下// 00010 >> 1 & 1 是1 所以turn(0, 1) 就是第一行第二个位置{ // 数字3 对应了00011 表示第1 和第2个位置的按一下step ++ ;turn (0, i);//使得第一行的形式改变 一定有一种 正好按住原状态中的暗灯}// 然后通过第一行按完之后的状态,按234行for (int i =0; i < 4; i ++ )for (int j = 0; j < 5;j ++ )if (g[i][j] == '0'){step ++;turn (i + 1, j); // 如果这个位置是灭的,就按下一行对应的位置}bool dark = false;for (int j = 0; j < 5; j ++ )if (g[4][j] == '0'){dark = true;break;}// 对于32种情况的这一种,如果所有的全亮就记录下步数(事实上只记录了最后一行是否dark)if (!dark) res = min(res, step);memcpy (g, backup, sizeof g);}if(res > 6) res = -1;cout << res << '\n';}return 0;
}
重点:
1.为什么五个位置是2^4却枚举到32 因为最差的情况是00000是0 最好的情况是11111是31 所以要到32
2.位运算中 ^1相当于取反 同0异1
3.把第一行的所有按法都枚举一遍 一定有一种是刚好与第一行的情况向对应的 不断更新答案即可
4,如果最后一行还有暗的话说明没救了
5.memcpy(a,b,sizeof b)是复制数组的用法
116. 飞行员兄弟 - AcWing题库
//一个把手改变,会使所在行列的所有把手全部反转
//特点:①在最优解里面每个把手只按一次,按两次没有区别//②按的顺序无关紧要,最终取决于这个把手按的次数!!!//思考这个题可以递推出来吗? 答案是:很难
//可以想一想,前面的题都是通过某种顺序,每一次都是影响一个灯泡,但是这个题
//不能使用前面的办法,因为操作一次会影响好多灯泡。所以想一想朴素做法//我们发现这个题的数据范围很小,所以尝试用暴力解决ac
//暴力思路:①16个开关,所有开关的状态数量想一想是多少? 答案是2^16!//状态数量即最大操作次数2^16(65536),既然也不大,那就①枚举所有的方案,
// int 是2^32
//然后按照这个方案来操作
//②如果可以实现把手全开,证明此方案合法
//③然后统计这个方案里面需要操作的把手数量
//④在所有能按的开关数量里取一个最小值//输出方案注意:若两种方案步数相同,按字典序(先按横坐标排序,再按纵坐标排序)#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;typedef pair<int,int> PII;
const int N=5;
char g[N][N],backup[N][N];//映射函数
int get(int x,int y){return x*4+y;//返回第x行第y列上的数是多少
}void turn_one(int x,int y){if(g[x][y]=='+') g[x][y]='-';else g[x][y]='+';
}void turn_all(int x,int y){for(int i=0;i<4;i++){turn_one(x,i);//关闭这一行所有的 turn_one(i,y);//关闭这一列的全部//xy被关闭了两次 相当于没变 }turn_one(x,y);//对xy也要改变
}int main(){for(int i=0;i<4;i++)for(int j=0;j<4;j++)cin>>g[i][j];vector<PII> res;//这是记录方案所需要的结构//枚举所有的方案for(int op=0;op<(1<<16);op++){//枚举每一种方案 1代表我们要去操作那个灯
//1111 1111 1111 1111 n个位置就到2^nvector<PII> temp;//temp里面存的是方案//先备份一下,为什么?因为这又不是最终方案,我们要把所有方案都试一遍,求最少的memcpy(backup,g,sizeof g);//枚举16个位置,进行操作for(int i=0;i<4;i++)for(int j=0;j<4;j++)if(op>>get(i,j)&1) //如果当前位置是1(我们在方案中准备操作)的话--get的作用就是返回二进制数中那一位是第几位,从而判断是否为1{temp.push_back({i,j});//按一下开关turn_all(i,j);}//判断所有灯泡是否全亮bool has_closed=false;for(int i=0;i<4;i++)for(int j=0;j<4;j++)if(g[i][j]=='+') has_closed=true;//有一个关闭就不行if(has_closed==false){//如果方案为空或者他的操作数大于我们刚存好的新的方案,那么就修改它if(res.empty()||res.size()>temp.size()) res=temp;//vector容器的操作}//还原回来,供下一个方案操作memcpy(g,backup,sizeof g);}//因为没说无解,所以可以猜想一下一定有解cout<<res.size()<<'\n';//这里的迭代函数就是一种简便写法,不要误解//另外原题下标从1开始,所以下面加1了for(auto op:res) cout<<op.x+1<<" "<<op.y+1<<'\n';return 0;
}
本题仍然是遍历所有的方案
1208. 翻硬币 - AcWing题库
#include <bits/stdc++.h>
using namespace std;
const int N=107;
char change(char &ch){if(ch=='*') return 'o';return '*';
}char a[N];
char b[N];
int main(){//读不进去可以尝试 cin>>s+1,cin>>s,cin.ignore(),getline(cin,a),cin.getline(a,N);cin.getline(a,N);cin.getline(b,N);// cin>>(a+1);// cin>>(b+1);int n = strlen(a);int step = 0;for(int i = 0; i < n; i++){if(a[i] != b[i]){a[i] = change(a[i]);if(i+1 < n) {a[i+1] = change(a[i+1]);}step++;}}cout << step;return 0;
}
读入数据:
1.char s[N] cin>>s+1; / cin>>s;
2.cin.ignore();getline(cin,a);
3.cin.getline(a,N);
790. 数的三次方根 - AcWing题库
#include <bits/stdc++.h>
using namespace std;double n,l,r,mid;
double eps=1e-8;//精度/100bool check(double mid,int m){//m次根下double c=1;while(m){c=c*mid;//s次根 m--;//c是mid的3次方 }
//要与别的条件扯上关系 在这里就是与n本身扯上关系if(c>=n)//如果mid的3次方大于等于n return true;//还可以再小 else//如果mid的3次方下雨n return false;//需要更大
}int main(){int m=3;cin>>n;
//设置二分边界l=-10000,r=10000;//设置大一点
//实数二分while (l + eps < r){//mid是a可以的值 double mid = (l + r) / 2;if (check(mid,m))r = mid;else l = mid;}//一般使用print//printf("%x.yf",n)//其中X是固定整数长度,小数点前的整数位数不够,会在前面补0//y是保留小数位数,不够补零printf("%.6lf",l);return 0;
}
796. 子矩阵的和 - AcWing题库
#include <bits/stdc++.h>
using namespace std;
const int N=1007;
//二维前缀和:预处理要与a[i][j]相关联
//预处理:s[i][j]=s[i-1][j]+s[i][j-1]+a[i][j]-s[i-1][j-1] -重复
//实现:(x1,y1)以左上角 (x2,y2)为右上角
//实现: s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1][y2] +重复//一维差分: 预处理b[i]=a[i]-a[i-1] b[i]改变后面的数都改 //二维差分:
//预处理:
//void insert(int x1,int y1,int x2,int y2){ +重复
//b[x1][y1]+=c;
//b[x2+1][y1]-=c;
//b[x1][y2+1]-=c;
//b[x2][y2]+=c;
//实现:
//b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1] -重复
int a[N][N];
int pre[N][N];
int main(){int n,m,q;cin>>n>>m>>q;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>a[i][j];}}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){pre[i][j]=pre[i-1][j]+pre[i][j-1]+a[i][j]-pre[i-1][j-1];}}while(q--){int x1,y1,x2,y2;cin>>x1>>y1>>x2>>y2;cout<<pre[x2][y2]-pre[x1-1][y2]-pre[x2][y1-1]+pre[x1-1][y1-1]<<'\n';}return 0;
}
730. 机器人跳跃问题 - AcWing题库
//递推解方程
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;int n;
int h[N];
double p[N];//存放2的次幂
//E(i+1)==2Ei-H(i+1)
//E(1)=2X-H(1)
//X>=(E(1)+H(1))/2
int main(){cin >> n;for(int i = 1; i <= n; i++) cin >> h[i];p[0] = 1;//2^0=1double res = 0;//假设最开始的能量值为res 最小值 for(int i = 1; i <= n; i++){p[i] = p[i - 1] * 2;//存2的次幂res += (1.0 / p[i]) * h[i];//最后Ei的能量值为0正好用完的时候最小}cout << ceil(res) << '\n';return 0;
}
1221. 四平方和 - AcWing题库
//写法1:纯暴力
#include <bits/stdc++.h>
using namespace std;
const int N = 5e6+7;
int n;
signed main () {int n;cin>>n;for(int a=0;a*a<=n;a++){for(int b=a;a*a+b*b<=n;b++){for(int c=b;a*a+b*b+c*c<=n;c++){int temp=n-(a*a+b*b+c*c);int d=sqrt(temp);if(d*d==temp&&d>=c){//记得这里取sqrt时还要判断一下是不是平方数printf("%d %d %d %d",a,b,c,d);return 0;}}}}return 0;
}
//模拟哈希表
#include <bits/stdc++.h>
using namespace std;
const int N = 5e6+7;
int n;
struct sum{int s,c,d;bool operator<(const sum &t)const{if(s!=t.s)return s<t.s;if(c!=t.c)return c<t.c;return d<t.d;}
}sum[N];
int m=1;
signed main () {int n;cin>>n;for(int c=0;c*c<=n;c++){for(int d=c;c*c+d*d<=n;d++){sum[m++]={c*c+d*d,c,d};}//存数}sort(sum,sum+m);for(int a=0;a*a<=n;a++){for(int b=a;a*a+b*b<=n;b++){int t=n-a*a-b*b;int l=1,r=m;while(l<r){int mid=l+r>>1;if(sum[mid].s>=t)r=mid;else l=mid+1;//尽量向左边找 }if(sum[l].s==t){printf("%d %d %d %d\n",a,b,sum[l].c,sum[l].d);return 0;}}}return 0;
}
//库函数哈希表
#include <bits/stdc++.h>
#define int long long
#define x first
#define y second
using namespace std;const int N = 5e6+7;
int n;
unordered_map<int, pair<int,int> > has;signed main() {cin >> n;for (int c = 0; c * c <= n; c++) {for (int d = c; c * c + d * d <= n; d++) {int t = c * c + d * d;if (has.count(t) == 0) has[t] = {c, d};}}for (int a = 0; a * a <= n; a++) {for (int b = a; a * a + b * b <= n; b++) {int t = n - a * a - b * b;if (has.count(t)) {printf("%d %d %d %d\n", a, b, has[t].x, has[t].y);return 0;}}}return 0;
}
//模拟哈希表
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N = 5e6 + 10;
int n;
int has[N * 2];//小技巧,避免pair,r[c^2+d^2]=c;可以推导出dint main(){cin >> n;memset(has, -1, sizeof has);for(int c = 0; c * c <= n; c++){for(int d = c; c * c + d * d <= n; d++){int t = c * c + d * d;if(has[t] == -1){has[t] = c;//只存下来c就可以了}}}for(int a = 0; a * a <= n; a ++){for(int b = a; a * a + b * b <= n; b++){int t = n - a * a - b * b;int c = has[t];if(has[t] == -1) continue;int d = sqrt(t - c * c);//不用判是不是平方数 因为一定是printf("%d %d %d %d\n", a, b, c, d);return 0;}}return 0;
}
1227. 分巧克力 - AcWing题库
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+7;
int n,k;
int h[N],w[N];
//mid是问的输出的答案 在return时候的条件尽量要与题目中的别的变量扯上关系
bool check(int mid){//表示巧克力的最大边长 int cnt=0;for(int i=1;i<=n;i++){//对于第i个巧克力 cnt+=(h[i]/mid)*(w[i]/mid);//长上能分出来的份数*宽上能分出来的份数}if(cnt>=k)return true;else return false;
}
void solve() {cin>>n>>k;//n个巧克力 分出来k块 求最大边长 for(int i=1;i<=n;i++){cin>>h[i]>>w[i];}int l=0,r=max(*max_element(h+1,h+n+1),*max_element(w+1,w+n+1)) ;while(l<r){int mid=(l+r+1)>>1;if(check(mid))l=mid;else r=mid-1;}cout<<r;
}
signed main() {int t = 1;while (t--) solve();return 0;
}
99. 激光炸弹 - AcWing题库
#include <iostream>
using namespace std;
const int N = 5010;
int n = 5005,m,r;
int sum[N][N];
int get (int x1,int y1,int x2,int y2) {return sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1];
}
int main () {cin >> m >> r;int maxx,maxy;for (int i = 1;i <= m;i++) {int x,y,w;cin >> x >> y >> w;x++,y++;maxx = max (maxx,x);maxy = max (maxy,y);sum[x][y] += w;}for (int i = 1;i <= n;i++) {for (int j = 1;j <= n;j++) sum[i][j] += sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1];}//预处理前缀和数组int ans = 0;for (int i = 1;i + r - 1 <= n;i++) {//右端点是i+r-1for (int j = 1;j + r - 1 <= n;j++) ans = max (ans,get (i,j,i + r - 1,j + r - 1));}if (r >= maxx && r >= maxy) {cout << sum[n][n] << '\n';return 0;}//特判了cout << ans << '\n';return 0;
}
1230. K倍区间 - AcWing题库
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=100007;
int a[N];
int cnt[N];//开一个cnt数组 cnt[i]表示的是截止到目前为止余数是i的数有多少个
signed main(){int n,k;cin>>n>>k;for(int i=1;i<=n;i++){cin>>a[i];a[i]+=a[i-1]; //前缀和
}int ans=0;for(int r=0;r<=n;r++){//枚举右端点 右端点也可能是0ans+=cnt[a[r]%k];//前面余数为a[r]%k的cnt[a[r]%k]个数都能做左端点//计算前面和 同余的个数 可以做左端点cnt[a[r]%k]++;//加上自身}cout<<ans;return 0;
}