false true
continue break
default(switch 语句)
(set<int>) :: iterator it = s.begin();it<s.end();
insert()// (set输入)
注释: Ctrl+/
向下复制一行:Ctrl+E
删除一行:Ctrl+D
整体代码缩进对齐: Ctrl+Shift+A
编辑运行:win+F11
选择全部:Ctrl+A
*(&a[1])@n //n为长度
(1)位与运算:先将n和1转化为4*8个bit的二进制,如何1代表true,0代表false,然后就是正常的与运算了
(2)有符号右移运算:本质上是转化为2进制补码然后右移,符号位补齐。虽然效果跟 n = n/2^1一样,但是这样比较快,因为这是直接对补码进行操作,少了转化等过程
(3)其实就是将一个数n 的k次幂转化为n2*x1*n2*x2…
例如求**410 = 48*404240
10 的二进制为1 0 1 0
求x的n次幂并对m取余数
typedef long long ll;
ll quick_pow(ll x,ll n,ll m){
ll res = 1;
while(n > 0){
if(n & 1) res = res * x % m;
x = x * x % m;
n >>= 1;//相当于n=n/2^1
}
return res;
}
2020 8 15 更新
//定义全1矩阵
struct M{
LL a[6][6];
M(){
for(int i=0;i<6;i++){
for(int j=0;j<6;j++){
a[i][j]=1;
}
}
}
};//这里有个分号
//矩阵乘法
//三重for循环,前两重是确定生成新矩阵的位置的,第三重是配合前两重做出乘法
M mMultiply(M m1,M m2){
M ans;
for(int i=0;i<6;i++){
for(int j=0;j<6;j++){
ans.a[i][j]=0;//因为我这里定义的是全1矩阵,所以需要初始化为0
for(int k=0;k<6;k++){
ans.a[i][j]=(ans.a[i][j]+m1.a[i][k]*m2.a[k][j])%MOD;
}
}
}
return ans;
}
//矩阵M的k次幂
M mPow(M m,int k){
M ans;
//初始化ans为单位矩阵
for(int i=0;i<6;i++){
for(int j=0;j<6;j++){
if(i==j)
ans.a[i][j]=1;
else
ans.a[i][j]=0;
}
}
while(k!=0){
if((k & 1)==1) {
ans = mMultiply(ans,m);
}
m = mMultiply(m,m);
k >>= 1;
}
return ans;
}
2020 8 16
题目:
输入一个n个节点的无根树的各条边,并指定一个根节点,要求把该树转化为有根树,输出各个节点的父亲编号。
#include
#include
using namespace std;
const int MAXN = 1000;
int n, p[MAXN];//p[i]代表i的父亲
vector<int> G[MAXN];
void dfs(int u, int fa) { //递归转化为以u为根的子树,u的父亲为fa
int d = G[u].size(); //节点u的相邻点的个数
for(int i = 0; i < d; ++i) { //循环遍历跟这个节点相连接的d个节点。
int v = G[u][i]; //节点u的第i个相邻点v
if(fa != v) dfs(v, p[v] = u); //把v的父亲节点设为u,然后递归转化为以v为根的子树
//一定要判断v是否和其父亲节点相等!
}
}
int main() {
cin >> n;
for(int i = 0; i < n-1; i++) { //输入n-1条边
int u, v;
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
int root;
cin >> root; //指定根节点。
p[root] = -1; //设定根节点的父亲节点为-1,代表根节点没有父亲节点。
dfs(root, -1);
for(int i = 0; i < n; ++i) {
cout << p[i] << endl;
}
return 0;
}
它是string类对象的成员函数,主要就是把string类型我们转化为char类型,方便我们调用char的函数
注意点:
注意:一定要使用strcpy()函数 等来操作方法c_str()返回的指针
比如:最好不要这样:
char* c;
string s=“1234”;
c = s.c_str(); //c最后指向的内容是垃圾,因为s对象被析构,其内容被处理
应该这样用:
char c[20];
string s=“1234”;
strcpy(c,s.c_str());
这样才不会出错,c_str()返回的是一个临时指针,不能对其进行操作
再举个例子
c_str() 以 char* 形式传回 string 内含字符串
如果一个函数要求char*参数,可以使用c_str()方法:
string s = “Hello World!”;
printf("%s", s.c_str());
//输出 “Hello World!”
该函数的格式为
int atoi(const char* str)
所以这就是我们为什么上面要结合c_str()的原因
配合例题学习
题目描述
100 可以表示为带分数的形式:100 = 3 + 69258 / 714。
还可以表示为:100 = 82 + 3546 / 197。
注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。
类似这样的带分数,100 有 11 种表示法。
输入
从标准输入读入一个正整数N (N< 1000*1000)
输出
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。
注意:不要求输出每个表示,只统计有多少表示法!
样例输入
100
样例输出
11
解题思路: 由题意可知1-9都要出现,且只能出现一次。自然想到用全排列来做,只需要加入一些判断即可(最后面的完整改进后完整代码有一些细节讲解)
注意事项: 在这个网站可以运行但是在蓝桥杯网站运可能会超时
参考代码(1):在这个网站可以运行,但是在蓝桥杯网站会超时,但是也可以学习一些一些函数及字符串处理
#include
#include
#include
using namespace std;
//这种方法超时,因为多次用了substr()来截取字符串,每次都要拷贝,和扫描
//过于耗时
int main(){
int n;
int ans = 0;
cin>>n;
std::string s = "123456789";
do {
for(int i=0;i<=7;i++){
string a=s.substr(0,i);
int inta = atoi(a.c_str());
if(inta >= n) break;
for (int j=1;j<=9-i-1;j++){
string b = s.substr(i,j);
string c = s.substr(i+j,9);
int intb = atoi(b.c_str());
int intc = atoi(c.c_str());
if(intb%intc==0&&inta+intb/intc==n) ans++;
}
}
} while(std::next_permutation(s.begin(),s.end())) ;
cout<<ans;
return 0;
}
题目描述
100 可以表示为带分数的形式:100 = 3 + 69258 / 714。
还可以表示为:100 = 82 + 3546 / 197。
注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。
类似这样的带分数,100 有 11 种表示法。
输入
从标准输入读入一个正整数N (N< 1000*1000)
输出
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。
注意:不要求输出每个表示,只统计有多少表示法!
样例输入
100
样例输出
11
解题思路: 由题意可知1-9都要出现,且只能出现一次。自然想到用全排列来做,只需要加入一些判断即可(最后面的完整改进后完整代码有一些细节讲解)
注意事项: 在这个网站可以运行但是在蓝桥杯网站运可能会超时
参考代码(1):在这个网站可以运行,但是在蓝桥杯网站会超时,但是也可以学习一些一些函数及字符串处理
改进代码:因为我们的atoi函数没有选择字符串长度就是只转化某个位置的字符串,所以我们需要自己定义一个转化函数。
//pos代表串开始的位置,len代表长度(包含pos)
int parse(const char *arr,int pos,int len){
int ans = 0;
int t = 1;
for(int i=pos-1+len-1;i>=pos-1;i--){//因为下标-1,因为包含pos-1
ans+=(arr[i]-'0')*t;
t*=10;
}
return ans;
}
改进后完整代码:
#include
#include
#include
using namespace std;
int parse(const char *arr,int pos,int len){
int ans = 0;
int t = 1;
for(int i=pos+len-1-1;i>=pos-1;i--){
ans+=(arr[i]-'0')*t;
t*=10;
}
return ans;
}
int main(){
int n;
int ans = 0;
cin>>n;
std::string s = "123456789";
do {
const char *str = s.c_str();// 由于s.c_str()这个函数返回的是const的类型,
// 所以我们上面定义的函数的参数也要是const
for(int i=0;i<=7;i++){
int inta = parse(str,1,i); // +号前的数字
if(inta >= n) break; // 如果单inta就>=n,那后面就不用遍历了,进入下一种排列
for (int j=1;j<=9-i-1;j++){
int intb=parse(str,i+1,j); // +和/中间的数字
int intc=parse(str,i+j+1,9-i-j); // / 后面的数字
if(intb%intc==0&&inta+intb/intc==n) ans++;
}
}
} while(std::next_permutation(s.begin(),s.end())) ;
cout<<ans;
return 0;
}
void i2s(int i,string &s){
stringstream ss;
ss<<i;
ss>>s;
}
具体例子:
//string -> double/int
#include
#include
using namespace std;
int main()
{
double dVal;
int iVal;
string str;
stringstream ss;
// string -> double
str = "123.456789";
ss << str;
ss >> dVal;
cout << "dVal: " << dVal << endl;
// string -> int
str = "654321";
ss.clear();
ss << str;
ss >> iVal;
cout << "iVal: " << iVal << endl;
return 0;
}
void f(int k){
if(k==9){//一种排列已经生成
if(check())
ans++;
}
for(int i=k;i<9;i++){
{ int t=a[i]; a[i]=a[k]; a[k]=t; }//替换
f(k+1);//递归
{ int t=a[i]; a[i]=a[k]; a[k]=t; } //回溯
//这里的递归,本质上是先纵再橫,我们只看从k到k+1,回溯是为了消除上一次替换的效果,然后尝试下一种。
}
}
传入的要提前排好序
#include
#include
using namespace std;
int ans=0;
int a[9]={1,2,3,4,5,6,7,8,9};
bool check(){
int A=a[0];
int b=a[1];
int c=a[2];
int def=a[3]*100+a[4]*10 +a[5];
int ghi=a[6]*100+a[7]*10+a[8];
if(b*ghi+def*c==(10-A)*c*ghi)
return true;
else
return false;
}
int main(){
do{
if(check()){
ans++;
}
}while(next_permutation(a,a+9));
cout<<ans;
return 0;
}
重点就是
#include
#include
#include
using namespace std;
int parse(const char *arr,int pos,int len){
int ans = 0;
int t = 1;
for(int i=pos+len-1-1;i>=pos-1;i--){
ans+=(arr[i]-'0')*t;
t*=10;
}
return ans;
}
int main(){
int n;
int ans = 0;
cin>>n;
std::string s = "123456789";
do {
const char *str = s.c_str();// 由于s.c_str()这个函数返回的是const的类型,
// 所以我们上面定义的函数的参数也要是const
for(int i=0;i<=7;i++){
int inta = parse(str,1,i); // +号前的数字
if(inta >= n) break; // 如果单inta就>=n,那后面就不用遍历了,进入下一种排列
for (int j=1;j<=9-i-1;j++){
int intb=parse(str,i+1,j); // +和/中间的数字
int intc=parse(str,i+j+1,9-i-j); // / 后面的数字
if(intb%intc==0&&inta+intb/intc==n) ans++;
}
}
} while(std::next_permutation(s.begin(),s.end())) ;
cout<<ans;
return 0;
}
参考:四平方和
https://blog.csdn.net/Hgg657046415/article/details/108096692
思路:
先选一个“标尺”,(每次都是左端点,最后在交换,左端点又变成了新的分界点)
用它把整个队列过一遍筛子,
以保证:其左边的元素都不大于它,其右边的元素都不小于它。
这样,排序问题就被分割为两个子区间。
再分别对子区间排序就可以了。
void swap(int a[], int i, int j)//交换
{
int t = a[i];
a[i] = a[j];
a[j] = t;
}
//定义标尺并把大于标尺的放在右侧,小于的放在左侧
int partition(int a[], int p, int r)
{
int i = p;//将开头赋值给 i
int j = r + 1;//将结尾+1赋值给j
int x = a[p];//标尺:每次都以左端点为标尺
while(1){
while(i<r && a[++i]<x);
while(a[--j]>x);
if(i>=j) break;
swap(a,i,j);
}
swap(a,p,j);
return j;
}
void quicksort(int a[], int p, int r)//p到r是需要排的范围
{
if(p<r){
int q = partition(a,p,r);//q才是标尺位置
quicksort(a,p,q-1);
quicksort(a,q+1,r);
}
}
做法:
类似于贪心,我们就是每一步都是最优解
代码:
#include
#include
using namespace std;
int ans=0;
int n;
int a[10001];
//找到并放回x在a[]中的下标
int pos(int x){
for(int i=1;i<=n;i++){
if(a[i]==x) return i;
}
return -1;
}
void swap(int i,int j){
int t=a[i];
a[i]=a[j];
a[j]=t;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<n;i++){
if(a[i]==i) continue;
else{//pos(i)为找到i在a[]中的下标
swap(i,pos(i));
ans++;
}
}
// for(int i=1;i<=n;i++){
// cout<
// }
// cout<
cout<<ans;
return 0;
}
做法:找到min ( a[k] ),并每个数减去(k-1),范围就会回到1~N-(k-1)
然后就是(1)了
计数排序:对数值范围特别大的不友好,但是对数值不大,但是数量多的特别优秀。
全部逆序个数
全部逆序个数
cout<<setiosflags(ios::fixed)<<setprecision(2)<<area;
//cout<<设置 ios 标志(ios::确定的)<<设置精度(精度值)<<输出内容;
对应题目:地宫取宝
https://blog.csdn.net/Hgg657046415/article/details/108181254
void dfs(参数1...){
//控制范围合理
if(超出递归范围) return;
if(判断是否到出口即可以达到要求的位置){
if(达到要求) ans++
dfs();
}
//上面是对这一层递归的判断
//递归部分
dfs();
dfs();
dfs();
}
//先定义一个数组
int direction[4][2]={{-1,0},
{1,0},
{0,-1},
{0,1}};
//实施方式
//四个方向的递归,(只看一个的话)
for(int i=0;i<4;i++){
int nx=x+direction[i][0];
int ny=y+direction[i][1];
//下面看情况而定
if(nx<0||nx>6||ny<0||ny>6) continue;
if(!sign[nx][ny]){
dfs(nx,ny);
}
}
对应题目:B_2016_07_剪邮票
https://blog.csdn.net/Hgg657046415/article/details/108180359
void dfs(int b[3][4],int i,int j){
b[i][j]=0;
if((i-1)>=0&&b[i-1][j]==1) dfs(b,i-1,j);
if((i+1)<=2&&b[i+1][j]==1) dfs(b,i+1,j);
if((j-1)>=0&&b[i][j-1]==1) dfs(b,i,j-1);
if((j+1)<=3&&b[i][j+1]==1) dfs(b,i,j+1);
}
辗转相除法又名广义欧几里得除法,是用来求解两个数的最大公约数的最佳算法之一。
算法原理:若a除以b的余数为r , 则有 (a , b) = ( b ,r ) ((a,b)表示a和b的最大公约数)
例:169与48的最大公约数求解过程
169 = 48 * 3 + 25 —— (169 , 48) = (48 , 25)
48 = 25 * 1 + 13 ——(48 , 25) = (25 , 13)
25 = 13 * 1 + 12
13 = 12 * 1 + 1
12 = 1 * 12 + 0 ——(12 , 1 ) = (1,0)= 1
故最大公约数为 1
#include
using namespace std;
int gcd(int a,int b) //定义函数gcd 计算
{
if(a%b==0)
return b; // 如果a能被整除b 则b为最大公约数
else
return gcd(b,a%b); //如果不能整除,则将b作为新的被除数,a作为新的除数,
}
int main() //定义主函数
{
int a,b; //定义变量 a,b
cin>>a>>b; //输入
cout<<gcd(a,b); //输出a,b的最大公约数
return 0;
}
int r=最大值;
int l=1;//最小值
while(r<=l){
mid=(l+r)/2;
...
操作区
...
if(满足条件){
l=mid+1;//也可以是r=mid-1;
}
else{
r=mid-1;
}
}
/*
标题: 分巧克力
儿童节那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。
小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形。
为了公平起见,小明需要从这 N 块巧克力中切出K块巧克力分给小朋友们。
切出的巧克力需要满足:
1. 形状是正方形,边长是整数
2. 大小相同
例如一块6x5的巧克力可以切出6块2x2的巧克力或者2块3x3的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小Hi计算出最大的边长是多少么?
输入
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含两个整数Hi和Wi。(1 <= Hi, Wi <= 100000)
输入保证每位小朋友至少能获得一块1x1的巧克力。
输出
输出切出的正方形巧克力最大可能的边长。
样例输入:
2 10
6 5
5 6
样例输出:
2
*/
#include
#include
using namespace std;
int main(){
int N,K;
int a[100000][2];
cin>>N>>K;
for(int i=0;i<N;i++){
cin>>a[i][0]>>a[i][1];
}
int S=0;
for(int i=0;i<N;i++){
S+=(a[i][0]*a[i][1]);
}
int r = abs(S/K)+1;
int l=1;
int ans=0; //记录最优的mid(最大的)
while(l<=r){
int mid=(l+r)/2;
int cut=0;
// 每块巧克力按照mid来切割
for(int i=0;i<N;i++){
cut+=(a[i][0]/mid)*(a[i][1]/mid);
}
if(cut>=K){
l=mid+1;//mid可以,继续尝试最优解
ans=mid; //记录本次最优解
}else{
r=mid-1;
}
}
cout<<ans<<endl;
return 0;
}
int f(int x,int y){
if(x==n||y==n){//在数组中x代表“哪一维”是纵向
return a[x][y];
}
else
a[x][y]+max(f(x+1,y),f(x+1,y+1));
}
有一个层数为n(n<=1000)的数字三角形。现有一只蚂蚁从顶层开始向下走,每走下一级,可向左下方向或右下方向走。求走到底层后它所经过数字的总和的最大值。
【输入格式】
第一个整数为n,一下n行为各层的数字。
【输出格式】
一个整数,即最大值。
【输入样例 】
5
1
6 3
8 2 6
2 1 6 5
3 2 4 7 6
【输出样例】
23
【样例说明】
最大值=1+3+6+6+7=23
#include
#include
using namespace std;
int a[1001][1001],n;
int f(int x,int y)// found the max number in a
{
if(x==n||y==n)
{
return a[x][y];
}
else{
return a[x][y]+max(f(x+1,y),f(x+1,y+1));
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
cin>>a[i][j];
}
}
cout<<f(1,1);//begin from (1,1)
}
有一个层数为n(n<=1000)的数字三角形。现有一只蚂蚁从顶层开始向下走,每走下一级,可向左下方向或右下方向走。求走到底层后它所经过数字的总和的最大值。
【输入格式】
第一个整数为n,一下n行为各层的数字。
【输出格式】
一个整数,即最大值。
【输入样例 】
5
1
6 3
8 2 6
2 1 6 5
3 2 4 7 6
【输出样例】
23
【样例说明】
最大值=1+3+6+6+7=23
我们可以看到其实在第三层的f(3,2)重复计算了两次
其实f(x,y)函数的意义就是,以(x,y)为顶点往下遍历所有路线中值最大的路线总值。
所以我们用一个二维数组记录一下,在上面仅仅4层一共15个f(x,y)的计算中就可以减少4次。
did[x][y]是记录以(x,y)为起点往下遍历的最大值路线所对对应的值
模板:
int f(int x,int y) {
if(x==n-1) {
return a[x][y];
}
if(did[x][y]){
return did[x][y];
}else {
did[x][y]=a[x][y]+max(f(x+1,y),f(x+1,y+1));
return did[x][y];
}
}
#include
#include
using namespace std;
int a[1001][1001],did[1001][1001],n;
int f(int x,int y) {
if(x==n-1) {
return a[x][y];
}
if(did[x][y]){
return did[x][y];
}else {
did[x][y]=a[x][y]+max(f(x+1,y),f(x+1,y+1));
return did[x][y];
}
}
int main() {
cin>>n;
for(int i=0; i<=n; i++) {
for(int j=0; j<=i; j++) {
cin>>a[i][j];
}
}
cout<<f(0,0);//begin from (0,0)
}