模拟 等腰三角形
#include<iostream>
using namespace std;
const int N = 1010;
char a[N][N];
char s[2 * N];
int main()
{
int n; cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n + i - 1; j++)
a[i][j] = '.';
int m = 0;
for (int i = 1; i < 600; i++)
{
int t = i;
if (t < 10)
{
s[++m] = t + '0';
}
else
{
char tn[4];
int k = 0;
while (t)
{
tn[k++] = t % 10;
t /= 10;
}
for (int x = k - 1; x >= 0; x--)
{
s[++m] = tn[x] + '0';
}
}
}
int t = 0;
for (int i = 1; i <= n; i++)
{
a[i][n - (i-1)] = s[++t];
}
for (int i = 2; i <= n + n - 1; i++)
{
a[n][i] = s[++t];
}
for (int i = n - 1; i > 1; i--)
{
a[i][n + i - 1] = s[++t];
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n + i - 1; j++)
{
cout << a[i][j];
}
cout << endl;
}
return 0;
}
排列数字
#include<iostream>
using namespace std;
const int N=10;
int n;
bool st[N];
int path[N];
void dfs(int u)
{
if(u==n)
{
for(int i=0;i<n;i++)
cout<<path[i]<<' ';
cout<<endl;
return;
}
for(int i=1;i<=n;i++)
{
if(!st[i])
{
path[u]=i;
st[i]=1;
dfs(u+1);
st[i]=0;
}
}
}
int main()
{
cin>>n;
dfs(0);
}
并查集
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int f[N];
int m, n;
int find(int x)
{
if (x != f[x])f[x] = find(f[x]);
return f[x];
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
f[i] = i;
while (m--)
{
char op; int a, b;
cin >> op >> a >> b;
if (op == 'M')
{
f[find(a)] = find(b);
}
else
{
if (find(a) == find(b))
cout << "Yes" << endl;
else
cout << "No" << endl;
}
}
}
第十三届真题
刷题统计
#include<iostream>
using namespace std;
#define ll long long
ll a,b,n;
int main()
{
cin>>a>>b>>n;
ll week=5*a+2*b;
ll day=n/week*7;
n%=week;
ll d[]={a,a,a,a,a,b,b};
for(int i=0;n>0;i++)
{
n-=d[i];
day++;
}
cout<<day<<endl;
}
修剪树木
#include<iostream>
using namespace std;
int main()
{
int n;cin>>n;
for(int i=1;i<=n/2;i++)
cout<<2*(n-i)<<endl;
for(int i=n/2+1;i<=n;i++)
cout<<2*(i-1)<<endl;
}
X进制减法
#pragma GCC optimize(2)
#include<iostream>
using namespace std;
#define ll long long
const int mod=1000000007;
const int N=1e5+10;
ll a[N],b[N];
int main()
{
ll ma,mb,n;
cin>>n>>ma;
for(int i=ma-1;i>=0;i--)
cin>>a[i];
cin>>mb;
for(int i=mb-1;i>=0;i--)
cin>>b[i];
int t,w;
ll mul=1,res=0;
for(int i=0;i<mb||i<ma;i++)
{
res=(res+(a[i]-b[i]+mod)%mod*mul%mod)%mod;
t=max(a[i],b[i]);
w=max(t+1,2);
mul=mul*w%mod;
}
cout<<res<<endl;
}
统计子矩阵
#pragma GCC optimize(2)
#include<iostream>
using namespace std;
#define ll long long
const int N=510;
ll n,m,k;
ll s[N][N];
ll calc(int x1,int y1,int x2,int y2)
{
return s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1];
}
int main()
{
cin>>n>>m>>k;
int x;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>x;
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+x;
}
ll cnt=0;
for(int l=1;l<=m;l++)
for(int r=l;r<=m;r++)
for(int i=1,j=1;i<=n;i++)
{
while(j<=i&&calc(j,l,i,r)>k)j++;
if(j<=i)cnt+=i-j+1;
}
cout<<cnt<<endl;
}
积木画 状态DP
#pragma GCC optimize(2)
#include<iostream>
using namespace std;
#define ll long long
const int mod=1000000007;
const int N=1e7+10;
int n;
ll g[4][4]=
{
{1,1,1,1},
{0,0,1,1},
{0,1,0,1},
{1,0,0,0}
};
int f[N][4];
int main()
{
cin>>n;
f[1][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<4;j++)
for(int k=0;k<4;k++)
{
f[i+1][k]=(f[i+1][k]+f[i][j]*g[j][k])%mod;
}
cout<<f[n+1][0]<<endl;
}
数的范围 二分
#include<iostream>
using namespace std;
int n,q;
const int N=1e5+10;
int a[N];
int main()
{
cin>>n>>q;
for(int i=0;i<n;i++)
cin>>a[i];
while(q--)
{
int k;cin>>k;
int l=0,r=n-1;
while(l<r)
{
int mid=(l+r)/2;
if(a[mid]>=k)r=mid;
else
l=mid+1;
}//如果有这个数字,返回它的第一个位置
if(a[l]!=k)
{
cout<<"-1 -1"<<endl;
}
else//返回该数字的最后一个位置
{
cout<<l<<' ';
l=0,r=n-1;
while(l<r)
{
int mid=(l+r+1)/2;
if(a[mid]<=k)l=mid;
else
r=mid-1;
}
cout<<l<<endl;
}
}
}
四平方和 二分+暴搜
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 5e6 + 10;
struct num
{
int c;
int d;
int sum;
}sum[N];
int n;
bool cmp(num a, num b)
{
if (a.sum != b.sum)return a.sum < b.sum;
if (a.c != b.c)return a.c < b.c;
if (a.d != b.d)return a.d < b.d;
}
int main()
{
cin >> n;
int m = 0;
for (int c = 0; c * c <= n; c++)
for (int d = c; d * d + c * c <= n; d++)
sum[m++] = { c,d,d * d + c * c };
sort(sum, sum + m,cmp);
for (int a = 0; a * a <= n; a++)
for (int b = a; b * b + a * a <= n; b++)
{
int t = n - (b * b + a * a);
int l = 0, r = m - 1;
while (l < r)
{
int mid = (l + r) / 2;
if (sum[mid].sum >= t)r = mid;
else l = mid + 1;
}
if (sum[l].sum == t)
{
cout << a << ' ' << b << ' ' << sum[l].c << ' ' << sum[l].d << endl;
}
}
}
三次方跟 实数二分
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int main()
{
double n;
cin>>n;
double l=-10000.0,r=10000.0;
while(r-l>1e-9)//这里尽量比精度稍微大一些
{
double mid=(l+r)/2;
if(mid*mid*mid>=n)r=mid;
else
l=mid;
}
printf("%.6lf",l);
}
走迷宫 BFS
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N=110;
char g[N][N];
int n,m;
struct pii
{
int x;
int y;
};
int d[N][N];
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
int bfs()
{
memset(d,-1,sizeof d);
queue<pii>q;
q.push({1,1});
d[1][1]=0;
while(!q.empty())
{
auto p=q.front();q.pop();
for(int i=0;i<4;i++)
{
int x=p.x+dx[i],y=p.y+dy[i];
if(g[x][y]=='0'&&d[x][y]==-1)
{
q.push({x,y});
d[x][y]=d[p.x][p.y]+1;
if(x==n&&y==m)
{
return d[x][y];
}
}
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>g[i][j];
cout<<bfs()<<endl;
}
八数码 bfs 该死的读写
#include <iostream>
#include <cstring>
#include <algorithm>
#include<queue>
#include <unordered_map>
using namespace std;
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
int bfs(string st)
{
string ed="12345678x";
unordered_map<string,int>d;
queue<string>q;
d[st]=0;
q.push(st);
while(!q.empty())
{
auto p=q.front();q.pop();
int distance=d[p];
int k=p.find('x');
int x=k/3,y=k%3;
for(int i=0;i<4;i++)
{
int a=x+dx[i],b=y+dy[i];
if(a<=2&&a>=0&&b>=0&&b<=2)
{
int t=a*3+b;
swap(p[t],p[k]);
if(!d.count(p))
{
d[p]=distance+1;
q.push(p);
if(p==ed)return d[p];
}
swap(p[t],p[k]);
}
}
}
return -1;
}
int main()
{
string st;
char s[2];
for(int i=0;i<9;i++)
{
cin>>s;
st+=s[0];
}
cout<<bfs(st)<<endl;
}
皇后问题 DFS
对角线映射公式为 u+i
反对角线映射公式为 n+(u-i)
#include<iostream>
using namespace std;
int n;
const int N=20;
char g[N][N];
bool col[N],dg[N],udg[N];
void dfs(int u)
{
if(u==n)
{
for(int i=0;i<n;i++)
cout<<g[i]<<endl;
cout<<endl;
return;
}
else
{
for(int i=0;i<n;i++)
{
if(!col[i]&&!dg[u+i]&&!udg[n+(u-i)])
{
g[u][i]='Q';
col[i]=dg[u+i]=udg[n+(u-i)]=true;
dfs(u+1);
col[i]=dg[u+i]=udg[n+(u-i)]=false;
g[u][i]='.';
}
}
}
}
int main()
{
cin>>n;
for (int i = 0; i < n; i ++ )
for(int j=0;j<n;j++)
g[i][j]='.';
dfs(0);
}
DP
背包问题
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n,m;
int v[N],w[N];
int f[N][N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i];
/*状态的转移可以看成
当容积为j的时候,首先不选择第i个物品,那我们可以直接从f[i-1][j]的情况转移过来
如果选择第i个物品,那么前提是当前的空间大小j足够装这个物品,那么我们可以从f[i-1][j-v[i]]转移过来
最终比较下两种方案的大小
*/
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
f[i][j]=f[i-1][j];
if(j-v[i]>=0)
f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
}
cout<<f[n][m]<<endl;
}
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n,m;
int v[N],w[N];
int f[N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i];
/*状态的转移可以看成
如果不选择 那么此时的f[j]就相当于上一轮的f[j]
如果选择 那么此时的f[j-v[i]]就相当于f[i-1][j-v[i]]
逆序遍历j是为了让上一轮的更新这一轮的f,防止前面的内容被污染
*/
for(int i=1;i<=n;i++)
for(int j=m;j>=v[i];j--)
{
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
cout<<f[m]<<endl;
}
数字三角形 线性dp
注意到权值可能为负数 因此不要用0来初始化数组
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
const int inf=-0x3f3f3f3f;
const int N=510;
int f[N][N];
int w[N][N];
int main()
{
cin>>n;
memset(f,inf,sizeof f);
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
cin>>w[i][j];
f[1][1]=w[1][1];
for(int i=2;i<=n;i++)
for(int j=1;j<=i;j++)
{
f[i][j]=max(f[i-1][j],f[i-1][j-1])+w[i][j];
}
int ma=inf;
for(int i=1;i<=n;i++)
ma=max(ma,f[n][i]);
cout<<ma<<endl;
}
最长上升子序列
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n;
int a[N],f[N];
int main()
{
cin>>n;int cnt=1;
cin>>a[1];f[1]=a[1];
/*
更新状态数组f[i]下标代表最长子序列的长度
那么数值代表长度为i的子序列的末尾数字
如果遇到的数字大于长度为cnt的末尾数字,那么长度+1,更新这个数字
如果不大于,那么就更新整个状态数组中第一个大于等于该数字的位置
*/
for (int i = 2; i <= n; i ++ )
{
cin>>a[i];
if(f[cnt]<a[i])
f[++cnt]=a[i];
else
{
int l=1,r=cnt;
while(l<r)
{
int mid=(l+r)/2;
if(f[mid]>=a[i])r=mid;
else
l=mid+1;
}
f[l]=a[i];
}
}
cout<<cnt<<endl;
}
数论
分解质因数
#include<iostream>
using namespace std;
int n;
void divide(int n)
{
for(int i=2;i*i<=n;i++)
if(n%i==0)
{
int s=0;
while(n%i==0)
{
n/=i;
s++;
}
cout<<i<<' '<<s<<endl;
}
if(n>1)cout<<n<<' '<<1<<endl;
}
int main()
{
cin>>n;
while(n--)
{
int m;cin>>m;
divide(m);
cout<<endl;
}
}
完全平方数 不开ll拿省四
#include<iostream>
#include <unordered_map>
using namespace std;
#define ll long long
ll divide(ll n)
{
ll ans=1;
for(ll i=2;i*i<=n;i++)
{
if(n%i==0)
{
int s=0;
while(n%i==0)
{
s++;
n/=i;
}
if(s&1)
ans*=i;
}
}
if(n>1)ans*=n;
return ans;
}
int main()
{
ll n;
cin>>n;
cout<<divide(n)<<endl;
}
筛质数
#include<iostream>
using namespace std;
const int N=1e6+10;
bool st[N];
int prime[N];
int cnt;
int main()
{
int n;cin>>n;
for(int i=2;i<=n;i++)
{
if(!st[i])prime[cnt++]=i;
for(int j=0;prime[j]*i<=n&&prime[j];j++)
{
st[prime[j]*i]=true;
if(i%prime[j]==0)break;
}
}
cout<<cnt<<endl;
}
约数个数
#include<iostream>
#include<unordered_map>
using namespace std;
const int mod=1e9+7;
unordered_map<int,int>prime;
int n;
int main()
{
cin>>n;
while(n--)
{
int x;cin>>x;
for(int i=2;i*i<=x;i++)
{
if(x%i==0)
{
while(x%i==0)
{
prime[i]++;
x/=i;
}
}
}
if(x>1)prime[x]++;
}
long long ans=1;
for(auto it=prime.begin();it!=prime.end();it++)
{
ans=ans*(1+it->second)%mod;
}
cout<<ans%mod<<endl;
}
约数之和
对于最高次数为n,最低次数为0的等比数列实现累加公式,公比为x
while(n--)
{
sum=sum*x+1;
}
#include<iostream>
#include<unordered_map>
using namespace std;
const int mod=1e9+7;
int main()
{
int t;cin>>t;
unordered_map<int,int>prime;
while(t--)
{
int x;cin>>x;
for(int i=2;i*i<=x;i++)
{
if(x%i==0)
{
while(x%i==0)
{
prime[i]++;
x/=i;
}
}
}
if(x>1)prime[x]++;
}
long long sum=1;
for(auto it=prime.begin();it!=prime.end();it++)
{
long long tmp=1;
int t=it->second;
while(t--)
{
tmp=(tmp*it->first+1)%mod;
}
sum=sum*tmp%mod;
}
cout<<sum<<endl;
}
最大公约数
#include<iostream>
using namespace std;
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int main()
{
int t;cin>>t;
while(t--)
{
int a,b;
cin>>a>>b;
cout<<gcd(a,b)<<endl;
}
}
BFS走迷宫类型
一般bfs tle了就说明是没有判断有没有走过该路径
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 110;
char g[N][N];
int dx[4] = { -1,0,1,0 }, dy[4] = { 0,1,0,-1 };
int sx, sy, ex, ey, n;
#define pii pair<int,int>
int d[N][N];
int bfs()
{
memset(d,-1,sizeof d);
queue<pii>q;
q.push({ sx,sy });
d[sx][sy] = 0;
while (q.size())
{
auto p = q.front(); q.pop();
int x = p.first, y = p.second;
for (int i = 0; i < 4; i++)
{
int tx = x + dx[i], ty = y + dy[i];
if (tx>=1&&tx<=n&&ty>=1&&ty<=n&&g[tx][ty]!=g[x][y]&&d[tx][ty]==-1)
{
q.push({ tx,ty });
d[tx][ty] = d[x][y]+1;
if (tx == ex && ty == ey)
return d[tx][ty];
}
}
}
return -1;
}
int main()
{
cin >> n; char ch;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
cin >> ch;
if (ch == ' ')
cin >> ch;
g[i][j] = ch;
if (g[i][j] == 'A')
sx = i, sy = j;
if (g[i][j] == 'B')
ex = i, ey = j;
}
cout << bfs() << endl;
}
砝码称重 DP+记忆化搜索剪枝暴搜
#include<iostream>
#include <unordered_map>
using namespace std;
const int N = 110;
const int M=2e5+10;
bool meo[N][M];
bool ans[M];
int n,cnt;
int a[N];
/*
加了剪枝的暴搜
当遇到u和sum相同的状态时可以停止向前递归搜索了
因为已经搜过一次 后续是重复搜索(可以提高时间效率)
深搜的三个路径分别是
(我们假设a-b为左边的砝码重量减去右边的砝码重量)
1.第i个砝码选择不放 u+1,sum
2.第i个砝码选择放在右边 u+1,sum-a[i]
又因为当sum为负时,我们可以颠倒砝码的位置来得到这种状态。
所以可以是u+1,abs(sum-a[i])
3.第i个砝码选择放在左边 u+1,sum+a[i]
*/
void dfs(int u,int sum)
{
if(meo[u][sum])return;
meo[u][sum]=true;
if(u==n&&!ans[sum])
{
cnt++;
ans[sum]=true;
return;
}
else
{
dfs(u+1,sum);
dfs(u+1,abs(sum-a[u]));
dfs(u+1,sum+a[u]);
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
dfs(0,0);
cout<<cnt-1<<endl;
}
#include<iostream>
using namespace std;
const int N=110;
const int M=2e5+10;
int a[N];
int f[N][M];
int main()
{
int n;cin>>n;
int sum=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum+=a[i];
}
f[0][0]=1;
//将考虑第0个砝码 并且总和为0的状态视为递推的地基
/*
集合f[i][j]代表的是该种方案是否存在
i代表的第i个砝码,j代表的是当前左边-右边的总和
因此状态的转移可以分为三种
1.f[i][j]=f[i-1][j] 第i个砝码不放
2.f[i][j]=f[i-1][j+a[i]] 第i个砝码放到了右边
3.f[i][j]=f[i-1][abs[j-a[i]] 第i个砝码放到了左边
当第i-1个状态的sum为负时 我们可以将两边砝码颠倒
这样同样能得到 abs[j-a[i]]的状态 这时再在颠倒的状态下将砝码放到右边
从而也可以得到f[i][j]的状态
总的来说我们只要从以上三种状态取其中一种存在的状态
即可得到当前f[i][j]状态
*/
for(int i=1;i<=n;i++)
for(int j=0;j<=sum;j++)
{
f[i][j]=f[i-1][j]||f[i-1][j+a[i]]||f[i-1][abs(j-a[i])];
}
int ans=0;
for(int i=1;i<=sum;i++)
if(f[n][i])ans++;
cout<<ans<<endl;
}
最大公约数
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
const int N=1e5+10;
int a[N];
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
sort(a+1,a+1+n);
int d=0;//0与b取gcd为b
for(int i=2;i<=n;i++)
{
d=gcd(d,a[i]-a[0]);
}
/*求一组数据的最大公约数,只需要先求前两个数的最大公约数d
然后拿d和后面的数求最大公约数并更新
*/
cout<<(a[n]-a[1])/g+1<<endl;
}