1 递推
约瑟夫环问题
https://blog.csdn.net/u011500062/article/details/72855826
https://www.luogu.com.cn/problem/P8671

点击查看代码
#include<iostream>
using namespace std;
int main(){
int n,k;
cin>>n>>k;
int ans=0;
for(int i=2;i<=n;i++) ans=(ans+k)%i;
cout<<ans+1;
}
2 排序
1) 最小字符串
实际用了双指针,比较大小,感觉还挺明显的,但是没想到
insert比+=和substr高效,不会超时,insert是在之前插入
https://www.luogu.com.cn/problem/P10910?contestId=238331

#include <string>
int main() {
std::string original = "Hello World";
std::string toInsert = "Beautiful Day";
original.insert(6, toInsert, 0, 9);
std::cout << original << std::endl;
return 0;
}
超时,不懂为啥从大到小排序不超时
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 10;
int n, m, ans,ans1,ans2, maxx, flag;
char a[maxn], b[maxn], c[maxn];
string s;
signed main() {
cin >> n >> m;
cin >> s;
for (int i = 0;i < m;i++)cin >> a[i];
for (int i = 0;i < m;i++)
{
int j = 0;
if (s[j] >= a[i])s = a[i] + s;
else {
//要加越界检查条件
//因为insert比substr和+=高i笑
while (j < s.size() && s[j] < a[i]) {
j++;
}
s = s.substr(0, j) + a[i] + s.substr(j, s.size() - j);
}
/*cout << "j=" << j << endl;*/
}
cout << s << endl;
}
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 10;
int n, m, ans, ans1, ans2, maxx, flag;
char a[maxn], b[maxn], c[maxn];
string s;
signed main() {
cin >> n >> m;
cin >> s;
for (int i = 0;i < m;i++)cin >> a[i];
sort(a, a + m);
int j = 0;
for (int i = 0;i < m;i++)
{
//要加越界检查条件
//需要取等 我们尽量让原来的字符在前面
while (j < s.size() && s[j] <=a[i]) {
j++;
}
string tmp = "";
tmp = a[i];
s = s.insert(j, tmp);
//字符串拼接的低效性,+=也低效
/*s = s.substr(0, j) + a[i] + s.substr(j, s.size() - j);*/
/*cout << "j=" << j << endl;*/
}
cout << s << endl;
}
2 两数之和是k倍数的数量
https://www.luogu.com.cn/problem/P8712
类似:
https://www.luogu.com.cn/problem/P8708 (更简单,用的双指针)
有点数论,a*pow(10,i)+b %k==0/k那么就是倍数
也类似于桶,当时写过一个把余数是多少存a[x]++,这里要记录二维余数是几,乘了10的多少次方
k倍区间
https://www.luogu.com.cn/problem/P8649
要注意临时变量,和全局变量重置
90:在乘的时候应该不断取模防止溢出
90pts
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 10;
int n, k,ans;
int a[maxn], t[15][maxn];
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> k;
for (int i = 1;i <=n;i++)
{
int x;
cin >> x;
a[i] = x;
for (int j = 1;j <= 11;j++)
{
//pow(10, 2)可能是99.999...,转成int就是99,导致错误
//pow返回double,可能会有精度问题
int y = a[i] *pow(10, j);
t[j][y% k]++;
//报错因为pow返回double,不能取模
}
}
for (int i = 1;i <= n;i++)
{
int x = a[i];
int cnt = 0;
while (x)
{
x /= 10;
cnt++;
}
int m = a[i] % k;
//注意x已经改变了,所以我们要注意设置临时变量
int c = (k - m) % k;
ans += t[cnt][c];
int y = (a[i] * pow(10, cnt));
if (y % k == c)
ans--;
}
cout << ans<< endl;
}
90->100
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 10;
int n, k,ans;
int a[maxn], t[15][maxn];
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> k;
for (int i = 1;i <=n;i++)
{
int x;
cin >> x;
a[i] = x;
for (int j = 1;j <= 14;j++)
{
//pow(10, 2)可能是99.999...,转成int就是99,导致错误
//pow返回double,可能会有精度问题
int j1 = j;
int s = 1;
while (j1--)
{
s *= 10;s = s % k;
}
int y = a[i] *s;
t[j][y% k]++;
//报错因为pow返回double,不能取模
}
}
for (int i = 1;i <= n;i++)
{
int x = a[i];
int cnt = 0;
while (x)
{
x /= 10;
cnt++;
}
int m = a[i] % k;
//注意x已经改变了,所以我们要注意设置临时变量
int c = (k - m) % k;
ans += t[cnt][c];
int s = 1;
int cnt1 = cnt;
while (cnt1--)
{
s *= 10;
s = s % k;
}
int y = (a[i] *s);
if (y % k == c)
ans--;
}
cout << ans<< endl;
}
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 5;
int n, k, mx = -1, ans = 0;
int a[N], fl[11][N];//第一维表示乘了10的几次方,第二位表示余数。
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> k;
for (int i = 1;i <= n;i++) {
cin >> a[i];
mx = max(mx, a[i]);
}
int cnt = 0;
for (;mx > 0;) {
cnt++;
mx /= 10;
}
//记录最大值的位数cnt
for (int i = 1;i <= n;i++) {
int asd = 10;
for (int j = 1;j <= cnt;j++) {
fl[j][(a[i] * asd) % k]++;
asd *= 10;
//因为至少拼接了一个数,至少扩大了十倍。
}
//每个数乘以 10^i 后对 k 取模的结果。
}
for (int i = 1;i <= n;i++) {
cnt = 0;
int aa = a[i];
for (;aa > 0;) {
cnt++;
aa /= 10;
}
//遍历每个数 a[i],计算其位数 cnt。
int b = a[i] % k, c = (k - b) % k;
//但我们期望 c 处于 [0, k - 1] 区间,因为b可能等于0
//余数之和为k或0
ans += fl[cnt][c];
int asd = 1;
for (int j = 1;j <= cnt;j++)
asd *= 10;
if ((a[i] * asd) % k == c)
ans--;
//会多计算自己和自己
}
cout << ans;
return 0;
}
修改了j>i+1->j>i
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 10;
int n, k, ans;
int t[15][maxn];
string a[maxn];
bool cmp(string o1, string o2)
{
if (o1.size() == o2.size())
return o1 < o2;
//o1 > o2 意味着若 o1 的字典序小于 o2,则返回 true,o1在前
else
return o1.size() < o2.size();
//cmp用小于号就是从小到大排序
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> k;
for (int i = 1;i <= n;i++)
{
cin >> a[i];
}
sort(a + 1, a + 1 + n, cmp);
for (int i = 1, j = n;i <= n;i++)
{
while (stoll(a[i] + a[j]) > k && j > i)
{//改成j>i就对了,j=i+1的时候需要进入循环,不然ans会错误+1
/*cout << "---" << a[i] + a[j] << endl;*/
j--;
}
//注意是先break后++
if (j <= i)break;
ans += j - i;
}
for (int i = 1, j = n;i <= n;i++)
{
while (stoll(a[j] + a[i]) > k && j > i)
//stoi返回int,stoll返回longlong,这里需要longlong,不然会re
{
//cout << "***" << a[i] + a[j] << endl;
j--;
}
if (j <= i)break;
ans += j - i;
}
cout << ans << endl;
}
80分
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 100010;
ll N, K, P, ANS;
ll A[MAXN];
inline ll connect(ll a, ll b){
return stoll(to_string(a) + to_string(b));
}
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> N >> K;
for (int i = 1; i <= N; ++i) cin >> A[i];
sort(A + 1, A + N + 1);
P = N;
for (int i = 1; i <= N; ++i){
while (P > 0 && connect(A[P], A[i]) > K) --P;
ANS += P, ANS -= (P >= i);
}
cout << ANS << endl;
return 0;
}
3 枚举
1)数正方形
不会
https://www.luogu.com.cn/problem/P8692
4 深搜
1)飞机降落
https://www.luogu.com.cn/problem/P9241
原本想用贪心,但是贪心策略不对,数据范围小,可以深搜,但是完全想不到这个题和深搜有关系。
要将 N 架飞机全部降落,那么考虑用深搜把所有情况全部试一遍,用 flag 记录是否有任意一种情况能用能将所有飞机全部落下。用 vis 数组记录该飞机是否降落。
剪枝
如果存在任意一个未降落飞机最晚降落时间小于当前时间,那么这个飞机一定不能降落,继续往下 搜一定不存在所有飞机降落的情况,则排除这种情况。
深搜枚举每一种情况,时间复杂度是阶乘
40%->100%代码
/*
做对了40%,有一个误解return是会返回到上一个函数调用的位置,而不是直接跳出函数到主函数
*/
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int maxn = 15;
int n;
int st[maxn];
struct p {
int t;
int d;
int l;
}a[maxn];
int dfs(int now, int cnt, int id)
{
if (cnt == n)
{
int ans = 0;
for (int i = 1;i <= n;i++)
{
if (st[i] == 1) ans++;
//错误写成赋值
}
if (ans == n)return 1;
}
st[id] = 1;
for (int i = 1;i <= n;i++)
{
if (st[i] == 1)continue;
if (a[i].t + a[i].d < now)break;
st[i] = 1;
if (dfs(max(now,a[i].t) + a[i].l, cnt + 1, i) == 1)
//if (dfs(now + a[i].l, cnt + 1, i) == 1)
//修改了这里,因为下一个要降落的飞机可能没到
//并且函数返回只是跳出当前,所以会返回到这,而不是主函数
return 1;
st[i] = 0;
}
return 0;
}
void solve()
{
cin >> n;
for (int i = 1;i <= n;i++)
cin >> a[i].t >> a[i].d >> a[i].l;
for (int i = 1;i <= n;i++)
{
memset(st, 0, sizeof st);
if (dfs(a[i].t + a[i].l, 1, i) == 1)
{
cout << "YES" << endl;
return;
}
}
cout << "NO" << endl;
return;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
solve();
}
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 15;
int n,bk[15];
struct plane{
int t, d, l;
}p[15];
bool dfs(int dep, int tim)
//dep 表示当前已经安排好降落的飞机数量,tim 表示当前的时间。
{
if (dep > n)
return 1;
for (int i = 1;i <= n;i++)
{
if (bk[i] || p[i].t + p[i].d < tim)//剪枝
continue;
bk[i] = 1;
if (dfs(dep + 1, max(tim, p[i].t) + p[i].l))//记得取max
{
/*bk[i] = 0;相当于找到了一个解*/
return 1;//有一个成功就返回1
}
bk[i] = 0;//记得去标记
}
return 0;
}
void solve()
{
cin >> n;
for (int i = 1;i <= n;i++)
{
cin >> p[i].t >> p[i].d >> p[i].l;
}
memset(bk, 0, sizeof bk);//记得清空
if (dfs(1,0))cout << "YES"<< endl;
else cout << "NO" << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--)solve();
}
自己写的
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 15;
int st[maxn];
int n;
struct plane {
int t;
int d;
int l;
}p[maxn];
int dfs(int cnt,int now)
{
if (cnt >= n + 1)
return 1;
for (int i = 1;i <= n;i++)
{
if (st[i] == 1)continue;
if (p[i].t + p[i].d < now)continue;
st[i] = 1;
if(dfs(cnt+1, max(p[i].t, now)+ p[i].l)==1)
return 1;
st[i] = 0;
}
return 0;
}
void solve()
{
cin >> n;
for (int i = 1;i <= n;i++)
cin >>p[i].t >> p[i].d >> p[i].l;
memset(st, 0, sizeof(st));
if (dfs(1, 0) == 1)
cout << "YES"<<endl;
else
cout << "NO" << endl;
return;
/*
for (int i = 1;i <= n;i++)
{
dfs(i,0,1)
不要传入id,直接传入数量,不然一部分深搜在函数内,一部分在主函数很乱
*/
}
signed main()
{
int T;
cin >> T;
while (T--)
{
solve();
}
}
2)危险系数
https://www.luogu.com.cn/problem/P8604
循环范围写错,写成1 <n,少了跳过
路径判断错误 应该是==0
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1010;
int g[maxn][maxn];
int cnt[maxn];
int st[maxn];
int n, m, u, v,ans,res;
void dfs(int now)
{
if (now == v)
{
ans++;
for (int i = 1;i <= n;i++)
//这里不对n要取等
{
if (st[i] == 1)
cnt[i]++;
}
}
else {
for (int i = 1;i <= n;i++)
{
if (g[now][i] == 0)continue;
//这里错误写为==1
if (st[i] == 1)continue;
st[i] = 1;
dfs(i);
st[i] = 0;
}
}
}
signed main()
{
cin >> n >> m;
for (int i = 1;i <= m;i++)
{
cin >> u >> v;
g[u][v] = g[v][u] = 1;
}
cin >> u >> v;
dfs(u);
if (ans > 0)
{
for (int i = 1;i <= n;i++)
{
if (cnt[i] == ans)
res++;
}
cout << --res << endl;
}
else
cout << -1 << endl;
}
3)五子棋盘平局问题
https://www.luogu.com.cn/problem/P10386
最后所有检查完才返回,注意全局变量局部变量更新
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5;
int dp[N][N];//存储棋盘状态1白2黑
int ans;//计方案数
bool check()//检查是否为平局
{
for (int i = 0;i < N;i++)//检查行
{
if (dp[i][0] == -1)continue;
bool ok = true;
for (int j = 1;j < N;j++)
if (dp[i][j] != dp[i][0])
{
ok = false;
break;
}
if (ok)return true;
//ok为真,有人赢了,返回true
}
for (int j = 0;j < N;j++)//检查列
{
if (dp[0][j] == -1)continue;
bool ok = true;
for (int i = 1;i < N;i++)
if (dp[i][j] != dp[0][j])
{
ok = false;
break;
}
if (ok)return true;
}
if (dp[0][0] != -1)//检查主对角线
{
bool ok = true;
for (int i = 1;i < N;i++)
if (dp[i][i] != dp[0][0])
{
ok = false;
break;
}
if (ok)return true;
}
if (dp[0][N - 1] != -1)//检查副对角线
{
bool ok = true;
for (int i = 1;i < N;i++)
if (dp[i][N - 1 - i] != dp[0][N - 1])
{
ok = false;
break;
}
if (ok)return true;
}
return false;
}
////ok为真,有人赢了,返回true
void dfs(int x, int y) {
if (check()) return; // 如果有人赢了
if (x == N) { // 如果已经遍历完所有格子,因为我们横着一行行放的,放满的时候是n-1行这里相当于已经到了下一行
int sum = 0;
for (int i = 0; i < N; i++) // 计算棋盘上白棋的数量
for (int j = 0; j < N; j++)
if (dp[i][j] == 1) sum += dp[i][j];
if (sum == 13) ans++; // 如果白棋数量为13,计数器增加
return;
}
int dx = x, dy = y;
if (y + 1 < N) dy++; else dy = 0, dx++; // 横着着一个个放,放到头后到下一行。按顺序放
//确定下一个待填充的位置
dp[x][y] = 1; // 在当前位置放白棋
dfs(dx, dy); // 递归继续搜索
dp[x][y] = 2; // 在当前位置放黑棋
dfs(dx, dy); // 递归继续搜索
dp[x][y] = -1; // 回溯,恢复当前位置为空
/*
当处理到某个位置时,前面的位置可能已经被设置过值,而后续的递归调用可能依赖于这些值是否正确。例如,当检查是否有人获胜时(check函数),棋盘的状态是所有已经处理过的位置的值。如果在递归调用中,某个位置的值没有被恢复,可能会导致后续的检查错误地认为棋盘状态已经被填充,从而提前终止搜索。
因为我们还没放满时就check了
*/
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
memset(dp, -1, sizeof(dp));//初始化设为-1;
dfs(0, 0);
cout << ans;
return 0;
}
枚举解法
#include<iostream>
using namespace std;
int ans = 0, a[26], r = 0, t = 0;
void pd() {
if (!((a[1] + a[2] + a[3] + a[4] + a[5]) % 5))return;
if (!((a[6] + a[7] + a[8] + a[9] + a[10]) % 5))return;
if (!((a[11] + a[12] + a[13] + a[14] + a[15]) % 5))return;
if (!((a[16] + a[17] + a[18] + a[19] + a[20]) % 5))return;
if (!((a[21] + a[22] + a[23] + a[24] + a[25]) % 5))return;
if (!((a[1] + a[6] + a[11] + a[16] + a[21]) % 5))return;
if (!((a[2] + a[7] + a[12] + a[17] + a[22]) % 5))return;
if (!((a[3] + a[8] + a[13] + a[18] + a[23]) % 5))return;
if (!((a[4] + a[9] + a[14] + a[19] + a[24]) % 5))return;
if (!((a[5] + a[10] + a[15] + a[20] + a[25]) % 5))return;
if (!((a[1] + a[7] + a[13] + a[19] + a[25]) % 5))return;
if (!((a[5] + a[9] + a[13] + a[17] + a[21]) % 5))return;
ans++;
}
void dfs(int k) {
/*r 表示数组中已经设置为 1 的元素个数。
t 表示数组中已经设置为 0 的元素个数。*/
if (k == 26) {
pd();
}
if (r <= 12) {
a[k] = 1;
r++;
dfs(k + 1);
r--;
}
if (t <= 11) {
a[k] = 0;
t++;
dfs(k + 1);
t--;
}
}
int main() {
dfs(1);
cout << ans;
return 0;
}
自己写的
#include<bits/stdc++.h>
using namespace std;
const int n = 5;
int dp[n][n];//存储棋盘状态1白2黑
int ans, flag;//计方案数
int check()
{
int flag ;
for (int i = 0;i < n;i++)
{//flag在检查每行时都要重置,这里导致错误
flag = 1;
if (dp[i][0] == -1)continue;
for (int j = 0;j < n;j++)
{
if (dp[i][j] != dp[i][0])
{flag = 0;
break;
}
}
if (flag == 1)
return 1;
}
for (int i = 0;i < n;i++)
{
if (dp[0][i] == -1)continue;
flag = 1;
for (int j = 0;j < n;j++)
{
if (dp[j][i] != dp[0][i])
{
flag = 0;
break;
}
}
if (flag == 1)
return 1;
}
/*
必须先if判断,再设置flag
因为当dp[0][0]是 - 1时,进入循环,执行break,
所以整个循环只运行i = 0的情况。
此时,如果主对角线全为 - 1的话,flag保持1吗?
例如,假设dp[0][0]是 - 1,循环立即break。此时,flag是1,然后判断是否返回1。
但实际上,主对角线全为 - 1的情况是否应该视为有人赢?
显然不是,因为此时所有元素都是空的。所以这会导致错误。flag = 1;*/
//for (int i = 0;i < n;i++)
//{
// if (dp[0][0] == -1)break;
// //如果没放应该跳出,而不是continue;
//
// if (dp[i][i] != dp[0][0])
// {
// flag = 0;
// break;
// }
//}
//if (flag == 1)
// return 1;
if(dp[0][0] != -1) {
flag = 1;
for (int i = 1; i < n; i++) {
if (dp[i][i] != dp[0][0]) {
flag=0;
break;
}
}
if (flag) return 1;
}
if (dp[0][n - 1] != -1) {
flag = 1;
for (int i = 1; i < n; i++) {
if (dp[i][n - 1 - i] != dp[0][n - 1]) {
flag = 0;
break;
}
}
if (flag) return 1;
}
//flag = 1;
// for (int i = 0;i < n;i++)
// {
// if (dp[0][n - 1] == -1)break;
//
// if (dp[0][n - 1] != dp[i][n - 1 - i])
// {
//flag = 0;
//break;
// }
//
//
// }
// /*之前已经全部返回了,这里的1可能是因为刚开始设置影响的,好像也不是反正加了不对*/
/*if (flag == 1)
return 1;*/
return 0;
//所有情况都检查完才return0
}
void dfs(int x, int y)
{
if (check() == 1)return;
if (x == n)
{
int cnt = 0;
for (int i = 0;i < n;i++)
{
for (int j = 0;j < n;j++)
{
if (dp[i][j] == 1)
cnt++;
}
}
if (cnt == 13)ans++;
return;
}
int dx = x;
int dy = y;
if (y == n - 1)
{
dx++, dy = 0;
}
else dy++;
//忘记写else
dp[x][y] = 1;
dfs(dx, dy);
dp[x][y] = 2;
dfs(dx, dy);
dp[x][y] = -1;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
memset(dp, -1, sizeof(dp));//初始化设为-1;
dfs(0, 0);
cout << ans;
return 0;
}
浙公网安备 33010602011771号