1.约瑟夫环问题
例
https://www.luogu.com.cn/problem/P1996
https://www.luogu.com.cn/problem/P8671
解题思路
1.朴实无华的暴力模拟
#include<iostream> using namespace std; int a[1000100]; signed main() { int n, k; cin >> n >> k; for (int i = 1; i <= n; i++)a[i] = 1; int cnt = 1; int flag = 0; int i = 1; while (1) { if (flag == n - 1)break; if (a[i] == 0)i++; else { if (cnt == k) { a[i] = 0; cnt = 1; flag++; i++; } else { cnt++; i++; } } if (i == n + 1)i = 1; } for (int i = 1; i <= n; i++) { if (a[i])cout << i; } return 0; }
但是显然,有大概率超时的风险(我就超时了)
2.循环链表法(
我暂时不会,跳过)
3.递归法
#include<iostream> using namespace std; int Josephus Circle(int N,int M,int i) { if(i==1) { return (M-1+N)%N; } return (Josephus Circle(N-1,M,i-1)+M)%N; } int main() { int N,M; cin>>N>>M; //10 3 for(int i=1;i<=N;i++) cout<<Josephus Circle(N,M,i)<<" "; return 0; }
对递归法,我的理解是:第N-1个人时第K-1个出局的人即为第N个人时第K个出局的人,于是一直往前推最终可以得出第?个人时第一个出局的人的编号,不断回溯最终得到所求值。其中得出结论的方法时:当每G掉一人时,对剩下的人重新编号,继续求下一个出局的人,可推得结论
4.递推公式
F(1)=0
F(n)=(F(n−1)+k)modn
#include <bits/stdc++.h> using namespace std; int main() { int n,k,s = 0; cin >> n >> k; for(int i = 2;i <= n;i++) { s=(s+k)%i; } cout << s+1; //求得最终出队的编号 return 0; }
2.约瑟夫
例
https://www.luogu.com.cn/problem/P1145
#include<iostream> using namespace std; signed main() { int k; cin >> k; int m = k; bool st = 0; while (!st) { m++; int point = 0; st = 1; for (int i = 0; i < k; i++) { point = (point - 1 + m) % (2*k-i); if (point < k) { st = 0; break; } if (i == k - 1)break; } } cout << m << endl;; }
思路
与约瑟夫环基本完全相同,但是不同的出题形式,首先给出k,那么总人数就是2*k,由于不能选到好人,所以m的值最小也是k+1,从m=k+1开始,设初始位置为0,依次判断第一个出列的编号,第二个出列的编号.....第k个出列的编号,由于N个人时,第K个出列的==第N-1个人时,第K-1个出列的当满足条件时,输出m
2.二进制枚举
例:
https://www.acwing.com/problem/content/94/
这是一种神奇的暴力枚举的方法,其原理我个人认为是状态压缩,把每个情况选与不选压缩为一个数,再使其每位按位与1,判断选于不选,来得出结果
核心步骤
int n=5;//5个小球 for(int i=0; i<(1<<n); i++) //从0~2^5-1个状态 { for(int j=0; j<n; j++) //遍历二进制的每一位 { if(i&(1<<j))//判断二进制第j位是否存在 { printf("%d ",j);//如果存在输出第j个元素 } } }
https://vjudge.csgrandeur.cn/problem/AtCoder-arc114_a/origin
#include <bits/stdc++.h> #define int long long #define PI pair<int,int> using namespace std; const int maxm=2e6+5; const int mod=1e9+7; bool isprime(int x){ for(int i=2;i*i<=x;i++){ if(x%i==0)return 0; } return 1; } void solve(){ vector<int>p; for(int i=2;i<=50;i++){ if(isprime(i)){ p.push_back(i); } } vector<int>a; int n;cin>>n; for(int i=1;i<=n;i++){ int x;cin>>x; a.push_back(x); } int c=p.size(); int ans=1e18; for(int i=0;i<(1<<c);i++){ int temp=1; for(int j=0;j<c;j++){ if(i>>j&1)temp*=p[j]; } int ok=1; for(auto s:a){ if(__gcd(temp,s)==1){ ok=0;break; } } if(ok)ans=min(ans,temp); } cout<<ans<<endl; } signed main(){ ios::sync_with_stdio(0); solve(); return 0; }
另一道例题
https://vjudge.csgrandeur.cn/contest/567178#problem/C
#include<iostream> #include<algorithm> #include<cstring> using namespace std; int c[20]; int a[20][20]; int cnt[20]; int n, m, x; bool st() { for (int i = 1; i <= m; i++) { if (cnt[i] < x)return false; } return true; } signed main() { scanf("%d%d%d", &n, &m, &x); for (int i = 1; i <= n; i++) { scanf("%d", &c[i]); for (int j = 1; j <= m; j++)scanf("%d", &a[i][j]); } int minn = 0x3f3f3f3f; for (int i = 1; i <= (1 << n); i++) { int time = 0; memset(cnt, 0, sizeof cnt); for (int j = 0; j < n; j++) { if (i & (1 << j)) { time += c[j + 1]; for (int k = 1; k <= m; k++)cnt[k] += a[j + 1][k]; } } if (st())minn = min(minn, time); } if(minn==0x3f3f3f3f)minn=-1; cout << minn << endl;; return 0; }
3.通.高精度乘法
1. 可计算两个超大数的乘积
#include<iostream>
#include<cstring>
using namespace std;
char a[100010], b[100010];
int a1[100010], b1[100010];
int c[100010];
signed main()
{
cin >> a >> b;
int lena = strlen(a);
int lenb = strlen(b);
for (int i = 1; i <= lena; i++)a1[i] = a[lena - i] - '0';
for (int i = 1; i <= lenb; i++)b1[i] = b[lenb - i] - '0';
for (int i = 1; i <= lena; i++)
for (int j = 1; j <= lenb; j++)
{
c[i + j - 1] += a1[i] * b1[j];
}
int len = lena + lenb;
for (int i = 1; i < len; i++)
{
if (c[i] > 9)
{
c[i + 1] += c[i] / 10;
c[i] %= 10;
}
}
while (c[len] == 0 && len > 1)len--;
for (int i = len; i >= 1; i--)cout << c[i];
}
2.可计算一超大数和一整数的乘积
#include<iostream>
#include<vector>
using namespace std;
vector<int>A;
vector<int>sub(vector<int>&A,int b)
{
vector<int>C;int t=0;
for(int i=0;i<A.size()||t;i++)
{
if(i<A.size())t+=A[i]*b;
C.push_back(t%10);
t/=10;
}
while(C.size()>1&&C.back()==0)C.pop_back();
return C;
}
signed main()
{
string a;int b;
cin>>a>>b;
for(int i=a.size()-1;i>=0;i--)
A.push_back(a[i]-'0');
auto C=sub(A,b);
for(int i=C.size()-1;i>=0;i--)
cout<<C[i];
}
应用例:
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
vector<int> mul(int b)
{
vector<int> A,C;
if(b==1)
{
C.push_back(1);
return C;
}
int d=b;
while(d)
{
A.push_back(d%10);
d/=10;
}
while(--b)
{
C.clear();
int t=0;
for(int i=0;i<A.size()||t;i++)
{
if(i<A.size())t+=A[i]*b;
C.push_back(t%10);
t/=10;
}
A=C;
}
return C;
}
vector<int> add(vector<int>&A,vector<int>&B)
{
vector<int> C;
int t=0;
for(int i=0;i<A.size()||i<B.size();i++)
{
if(i<A.size()) t+=A[i];
if(i<B.size()) t+=B[i];
C.push_back(t%10);
t/=10;
}
if(t) C.push_back(1);
return C;
}
int main()
{
int b;
cin>>b;
auto C=mul(b);
vector<int> E,F;
E.push_back(0);
while(1)
{
if(b==0)
{
break;
}
auto C=mul(b);
F=add(C,E);
E=F;
b--;
}
for(int i=E.size()-1;i>=0;i--)
{
printf("%d",E[i]);
}
}
汉诺塔问题
根据观察可知:将n个圆盘从A盘移到C盘至少需要(2^n)-步
递推公式为F【n】=2 * F【n-1】+1
高精度+汉诺塔
#include<iostream>
#include<vector>
using namespace std;
vector<int>C;
vector<int>A;
int n;
signed main()
{
cin >> n;
int k = n + 1;
A.push_back(2);
int ans=0;
int t=0;
while (--k)
{
ans++;
if (k == 1) t = -2;
else t = 0;
C.clear();
for (int i = 0; i < A.size() || t; i++)
{
if (i<A.size())t += A[i] * 2;
C.push_back(t % 10);
t /= 10;
}
while (C.size() > 1 && C.back() == 0) C.pop_back();
// A.clear();
A = C;
}
// cout<<ans<<endl;
for (int i = A.size() - 1; i >= 0; i--)cout << A[i];
return 0;
}//观察可知:双塔问题的答案应为(2^(n+1))-2
数楼梯
(坑题一道,我上手就是一个暴搜然后完美的TLE了)
#include<iostream>
using namespace std;
int n;
int ans;
void dfs(int u)
{
if (u == n)
{
ans++;
return;
}
if(u>n)return;
dfs(u + 1);
dfs(u + 2);
}
signed main()
{
scanf("%d", &n);
dfs(0);
printf("%d\n", ans);
return 0;
}
想了想发现另有玄机:
随着n不断增加,不同阶楼梯的上法为一个斐波那契数列(我果断再交,再次寄掉)
#include <iostream>
using namespace std;
const int N = 5010;
long long f[N];
int main()
{
int n;
cin >> n;
f[1] = 1;
f[2] = 2;
for(int i=3;i<=n;i++)
{
f[i] = f[i - 2] + f[i - 1] ;
}
cout << f[n] << endl;
}
最后只能高精度
#include<vector>
#include<iostream>
using namespace std;
vector<int>f[5010];
int n;
vector<int>add(vector<int>& a, vector<int>&b)
{
int t = 0;
vector<int>C;
for (int i = 0; i < a.size() || i < b.size(); i++)
{
if (a.size())t += a[i];
if (b.size())t += b[i];
C.push_back(t % 10);
t /= 10;
}
if (t)C.push_back(t);
return C;
}
signed main()
{
scanf("%d", &n);
f[1].push_back(1);
f[2].push_back(2);
for (int i = 3; i <= n; i++)
{
f[i] = add(f[i - 1], f[i - 2]);
}
for (int i = f[n].size() - 1; i >= 0; i--)cout << f[n][i];
return 0;
}//涉及到一个vector类型的数组的运用,之前没这么用过