1 递推

约瑟夫环问题

https://blog.csdn.net/u011500062/article/details/72855826
https://www.luogu.com.cn/problem/P8671
image

点击查看代码
#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
image

#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;
}
#### 3) 整数小拼接 ####stoi和stoll注意数据范围,注意更新答案和跳出边界的时间
修改了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;
}

4)

https://www.luogu.com.cn/problem/P10419

posted on 2025-03-29 21:27  Hoshino1  阅读(17)  评论(0)    收藏  举报