DFS and 记忆化搜索
CF1851E Nastya and Potions - 洛谷
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int N = 2e5 + 7;
#define vi vector<int>
int c[N];
int sz[N];
vi t[N];
int dfs(int u) {
if (sz[u] != -1) return sz[u]; //剪枝
int ans = 0;//另开一个数记录子节点之和
for (int v : t[u])
ans += dfs(v);
return sz[u] = min(ans, c[u]);
}
void solve() {
int n, k, m; cin >> n >> k;
for (int i = 1; i <= n; ++i) {
cin >> c[i];
sz[i] = -1;
t[i].clear();
}
for (int i = 1; i <= k; ++i) {
int p; cin >> p;
c[p] = 0;
sz[p] = 0;
}
for (int i = 1; i <= n; ++i) {
cin >> m;
for (int j = 1; j <= m; ++j) {
int v; cin >> v;
if (c[i] == 0) continue;
t[i].push_back(v);
}
if(!m)sz[i] = c[i];
}
for (int i = 1; i <= n; ++i)
cout << dfs(i) << " \n"[i == n];
}
signed main() {
ios :: sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--) solve();
return 0;
}
[数的划分 - 洛谷](https://www.luogu.com.cn/problem/P1025
递归 + 剪枝
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
int n, k;
int ans;
void dfs(int s, int cnt, int last){
if(s == n && cnt == k){
++ans; return ;
}
if(s >= n || cnt >= k)return ;//剪枝
for(int i = last; i <= n - s; ++i)//n - s相当于剪枝了// last 避免重复计数
dfs(s + i, cnt + 1, i);
}
void solve(){
cin >> n >> k;
dfs(0, 0, 1);
cout << ans << endl;
}
signed main(){
ios :: sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
//cin >> T;
while(T--)solve();
return 0;
}
P9425 [蓝桥杯 2023 国 B] AB 路线 - 洛谷
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define vi vector<int>
#define vii vector<vi>
#define pii pair<int, int>
const int mod = 1e9 + 7;
const int N = 1e3 + 5;
int n, m, k, ans = -1;
char s[N][N];
int dp[N][N][11][2];//(i, j), k, A0,B1
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
struct node{
int x, y;
int cnt;
int stp;
char ch;
};
void bfs(){
queue<node>q;
q.push({1, 1, 1, 0, s[1][1]});
while(!q.empty()){
//auto [x, y, cnt, stp, ch] = q.front(); q.pop();
node t = q.front(); q.pop();
if(t.x == n && t.y == m){
ans = t.stp;
return ;
}
for(int i = 0; i < 4; ++i){
int nx = t.x + dx[i];
int ny = t.y + dy[i];
if(nx < 1 || nx > n || ny < 1 || ny > m)continue;
if(t.ch == s[nx][ny] && t.cnt < k){//相同字符可以走的条件
if(!dp[nx][ny][t.cnt + 1][s[nx][ny] - 'A']){
dp[nx][ny][t.cnt + 1][s[nx][ny] - 'A'] = 1;
q.push({nx, ny, t.cnt + 1, t.stp + 1, s[nx][ny]});
}
}
else if(t.ch != s[nx][ny] && t.cnt == k){//不同字符可以走的条件
if(!dp[nx][ny][1][s[nx][ny] - 'A']){
dp[nx][ny][1][s[nx][ny] - 'A'] = 1;
q.push({nx, ny, 1, t.stp + 1, s[nx][ny]});
}
}
}
}
}
void solve(){
cin >> n >> m >> k;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)cin >> s[i][j];
memset(dp, 0, sizeof dp);
dp[1][1][1][0] = 0;
bfs();
cout << ans << endl;
}
signed main(){
ios :: sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
//cin >> T;
while(T--)solve();
return 0;
}
P1486 - 走格子 Ⅲ - ZJHUOJ
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define vi vector<int>
#define vii vector<vi>
#define pii pair<int, int>
const int mod = 1e9 + 7;
const int N = 1e3 + 5;
int n, m;
int dp[N][N][4][3];//(i, j)点的方向以及方向的次数
int dfs(int x, int y, int d, int c){//0起点, 1右, 2下, 3上
if(x == n && y == m)return 1;
if(dp[x][y][d][c] != -1)return dp[x][y][d][c];
int res = 0;
if(c == 2){ //等于2只有一种情况
if (d == 1) {//右
if(x + 1 <= n) res = (res + dfs(x + 1, y, 2, 1)) % mod;//下
if(x - 1 >= 1) res = (res + dfs(x - 1, y, 3, 1)) % mod;//上
}
else{//上/下
if(y + 1 <= m) res = (res + dfs(x, y + 1, 1, 1)) % mod;//向右
}
}
else {
if(d == 0){
if(y + 1 <= m)res = (res + dfs(x, y + 1, 1, 1)) % mod;//向右
if(x + 1 <= n)res = (res + dfs(x + 1, y, 2, 1)) % mod;//向下
}
else if(d == 1){//向右
if(y + 1 <= m)res = (res + dfs(x, y + 1, 1, c + 1)) % mod;//向右
if(x + 1 <= n)res = (res + dfs(x + 1, y, 2, 1)) % mod;//向下
if(x - 1 >= 1) res = (res + dfs(x - 1, y, 3, 1)) % mod;//向上
}
else if(d == 2){//向下
if(x + 1 <= n)res = (res + dfs(x + 1, y, 2, c + 1)) % mod;//向下
if(y + 1 <= m)res = (res + dfs(x, y + 1, 1, 1)) % mod;//向右
}
else {//向上
if(x - 1 >= 1)res = (res + dfs(x - 1, y, 3, c + 1)) % mod;//向上
if(y + 1 <= m)res = (res + dfs(x, y + 1, 1, 1)) % mod;//向右
}
}
dp[x][y][d][c] = res;//记忆
return res;
}
void solve(){
cin >> n >> m;
memset(dp, -1, sizeof dp);
cout << dfs(1, 1, 0, 0) % mod << endl;
}
signed main(){
ios :: sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
//cin >> T;
while(T--)solve();
return 0;
}
优化版代码:
// dp[i, j]定义:从上往下或从下往上到达(i,j)位置的路径数
// 状态转移方程:dp[i, j] = dp[i ± 1, j - 1] + dp[i ± 2,j-2]
#include<bits/stdc++.h>
using namespace std;
const int mxn = 1e3+5, MOD = 1e9+7;
int n, m, dp[mxn][mxn];
void f(int x, int y){
for(int i = -2; i <= 2; ++i)
for(int j = 1; i && j <= 2; ++j)
dp[x][y] = (dp[x][y] + dp[x+i][y-j])%MOD;
}
int main(){
scanf("%d%d", &n, &m);
n += 1, m += 1;
for(int i = 2; i <= min(n, 4); ++i) dp[i][2] = 1;
for(int j = 3; j <= m; ++j) // 必须按列更新
for(int i = 2; i <= n; ++i) f(i,j);
// print();
printf("%d\n", ((dp[n][m]+dp[n][m-1])%MOD+dp[n][m-2])%MOD);
return 0;
}
归并排序(并找逆序): 归并排序使用分治策略,将数组递归地一分为二,直到每个子数组只包含一个元素。
map<string,int>mp
map在迭代器中才可以用first,second
不然就用mp[i]=s;
map的直接赋值法{
m1["def"] = 2;||
m2.insert({ "abc", 1 });
}
归并排列过程示例
![[Pasted image 20250411200333.png]]
从上往下递归,从下往上分解
类似dfs的树,and每归并一层都会更新当前排序,然后被上一层作为排序的依据
归并排序
![[Pasted image 20250411202802.png
P5149 会议座位 - 洛谷
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
//#define int long long
#define ll long long
const int mxn=1e5+7;
int a[mxn],n,b[mxn];//b辅助(临时)数组
ll ans;
string s;
map<string,int>mp;//分治、归并排序
void merge(int l,int r){
if(r<=l)return;
int mid=l+(r-l>>1);//1LL
merge(l,mid); merge(mid+1,r);
int i=l,j=mid+1,k=l;
while(i<=mid&&j<=r){//先赋值再++
if(a[i]<a[j])b[k++]=a[i++];//左右两边较小的排在前面
else b[k++]=a[j++],ans+=mid-i+1;//要比较的两个子序列都已经排好序//在子序列的每个子序列合并时也已经找好逆序数
}
while(i<=mid)b[k++]=a[i++];
while(j<=r)b[k++]=a[j++];//左右两边剩余没排完的排上去
for(int i=l;i<=r;++i)a[i]=b[i];//更新原数组的排序
return;
}
void solve(){
cin>>n;
for(int i=1;i<=n;++i){
cin>>s;
mp[s]=i;
// mp.insert({s,i});
}
for(int i=1;i<=n;++i){
cin>>s;
a[mp[s]]=i;
}
merge(1,n);
cout<<ans<<endl;
return ;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T=1;
//cin>>T;
while(T--)solve();
return 0;
}
相信奇迹的人,本身就和奇迹一样了不起。——笛亚 《星游记》
next_permutation()用法
时间复杂度是O(n!)注意剪枝
#include<bits/stdc++.h>
using namespace std;
signed main(){
int n,a[10]={1,2,3,4,5};
do{
for(int i=0;i<=4;++i)cout<<a[i];
cout<<endl;
}while(next_permutation(a,a+5));//最好用一个数组来存数
return 0;
}
注:next_permutation()函数从当前的全排列开始,逐个输出更大的去安排列,而不是输出所有的全排列
如果想要得到全排列可以先用sort排序
要对杨辉三角数敏感
杨辉三角代码
(now number=同列上一层的前两个数字相加)
c[1][1]=1;//最左上角的数初始化为1
for(int i=2;i<=n;i++)
for(int j=1;j<=i;j++) c[i][j]=c[i-1][j]+c[i-1][j-1];//每个数都等于它肩上两数之和
法二
//下面构造杨辉三角(即组合数表)
pc[0]=pc[n-1]=1; //从0开始,杨辉三角性质,两边都是1
if (n>1)for(int i=1;i*2<n;i++)
pc[i]=pc[n-1-i]=(n-i)*pc[i-1]/i; //利用杨辉三角对称性和组合数公式计算
P1118 Backward Digit Sums G/S - 洛谷
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
//#define int long long
//#define ll long long
//#define Ll unsigned long long
//const int mxn=3e5+7;
//string s;
int n,m,t,a[20],b[20][20];
void solve(){
cin>>n>>m;
a[1]=1, b[1][1]=1;//最后一行各系数恰好是杨辉三角数!
for(int i=2;i<=n;++i){//第i层,杨辉三角是从1开始的
a[i]=i;
for(int j=1;j<=i;++j)b[i][j]=b[i-1][j]+b[i-1][j-1];}
do{
t=0;
for(int i=1;i<=n;++i){
t+=a[i]*b[n][i];
if(t>m)break;
}
if(t==m){
for(int i=1;i<=n;++i)cout<<a[i]<<(i==n?"\n":" ");
break;
}
}while(next_permutation(a+1,a+1+n));
return ;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T=1;
//cin>>T;
while(T--)solve();
return 0;
}
代码2
int n,m,a[20],b[20][20],vis[20];
void dfs(int k,int t){//t要放在这里,不然会一直被加
if(t>m)return;
if(k==n+1&&t==m){
for(int i=1;i<=n;++i)cout<<a[i]<<(i==n?"\n":" ");
exit(0) ;//正常运行并退出程序
}
if(k==n+1)return;
for(int i=1;i<=n;++i){
if(!vis[i]){
vis[i]=1;//在for循环里标记!
a[k]=i;
dfs(k+1,t+i*b[n][k]);
vis[i]=0;//每次dfs完赶紧复位!return后把vis变成0供下一次根节点搜索的使用
}
}
}
void solve(){
cin>>n>>m;
b[1][1]=1;
for(int i=2;i<=n;++i)
for(int j=1;j<=i;++j)
b[i][j]=b[i-1][j-1]+b[i-1][j];
dfs(1,0);
return ;
}
方格取数 - 洛谷
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
//#define int long long
//#define ll long long
//vector<pair<int,int>>E[mxn];
//double ans=DBL_MAX;//double的最大值
//vector<vector<int>>e(mxn);
//typedef pair<int, int>PII;
const int mxn=20;
int dp[mxn][mxn][mxn],a[mxn][mxn],w[mxn][mxn],n,m,ans;
// int read(){
// int x=0; bool t=false; char ch=getchar();
// while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
// if(ch=='-')t=true, ch=getchar();
// while(ch<='9'&&ch>'0')x=x*10+ch-48,ch=getchar();
// return t? -x:x;
// }//快读
void solve(){
cin>>n;
int a,b,c;
while(cin>>a>>b>>c,a|b|c){
w[a][b]=c;
// cout<<w[a][b]<<endl;
}
int x2,y2;
for(int k=2;k<=2*n;++k){//k<=2*n是因为省略了纵坐标循环,实际上至少走2*(n-1)步才能到达
for(int x1=1;x1<=n;++x1){
for(int x2=1;x2<=n;++x2){
int y1=k-x1,y2=k-x2;
int t=w[x1][y1];
if(x1!=x2)t+=w[x2][y2];
int &x=dp[k][x1][x2];
x=max(x,dp[k-1][x1-1][x2]+t);
x=max(x,dp[k-1][x1-1][x2-1]+t);//dd x是由上一步继承来的,y是现在的位置,则表示的位移是dd
x=max(x,dp[k-1][x1][x2-1]+t);
x=max(x,dp[k-1][x1][x2]+t);//rr
//在x1,x2的移动一步中选择最大的那个,这一步状态是由上一步得来的
}
}
}
cout<<dp[2*n][n][n]<<endl;
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
//cin >> T;
while (T--)solve();
return 0;
}
C - Concat (X-th)
dfs+排序
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define int long long
#define vi vector<int>
#define vii vector<vi>
const int N = 1e6 + 10;
string res[N], s[N];
int tot, n, k, x;
void dfs(int dep, string t){
if(dep > k){res[++tot] = t; return;}
for(int i = 1; i <= n; ++i)dfs(dep + 1, t + s[i]);//这样的dfs可以遍历所有
}
void solve(){
cin >> n >> k >> x;
for(int i = 1; i <= n; ++i)cin >> s[i];
dfs(1, "");
sort(res + 1, res + 1 + tot);
cout << res[x] << endl;
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while(T --)solve();
return 0;
}
D - Goin' to the Zoo
DFS
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define pii pair<int, int>
#define vi vector<int>
#define vii vector<vi>
#define lowbit(x) (x & (-x))
const int N = 1e2 + 7;
int n, m;
int w[15], k[N], a[N][N];
int v[N];//每个动物园看了几次
int ans = 1e18;
void dfs(int t, int sm){
//正在枚举第t个动物园的情况,已经花了sm元
if(t == n + 1){
for(int i = 1; i <= m; ++i){
//计算第i个动物看了几次;
int tmp = 0;
for(int j = 1; j <= k[i]; ++j)
tmp += v[a[i][j]];
if(tmp < 2)return ;
}
ans = min(ans, sm);
return ;
}
for(int x = 0; x <= 2; ++x){
v[t] = x;
dfs(t + 1, sm + x * w[t]);
//对于每个园的x = 0, 1, 2次的每种情况继续往下遍历
}
}
void solve(){
cin >> n >> m;
for(int i = 1; i <= n; ++i)cin >> w[i];
for(int i = 1; i <= m; ++i){
cin >> k[i];
for(int j = 1; j <= k[i]; ++j)
cin >> a[i][j];//第i个动物出现在第j个园的号
}
dfs(1, 0);
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
//cin >> T;
while(T--) solve();
return 0;
}
D - 2x2 Erasing 2
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define int long long
#define vi vector<int>
#define vii vector<vi>
const int inf = 0x3f3f3f3f;
const int N = 10;
int cnt = 0, n, m, ans = inf;
char s[N][N];//在算法竞赛中,对于网格类问题通常推荐使用char数组而不是string数组
void dfs(int x, int y, int cnt){
if(cnt >= ans)return;
if(y == m)dfs(x + 1, 1, cnt);
if(x == n){ans = min(ans, cnt); return;}
if(s[x][y] == '.' || s[x][y + 1] == '.' || s[x + 1][y] == '.' || s[x + 1][y + 1] == '.')dfs(x, y + 1, cnt);
else {
s[x + 1][y + 1] = '.';
dfs(x, y + 1, cnt + 1);//修改后继续遍历下一个数
s[x + 1][y + 1] = '#';
s[x + 1][y] = '.';
dfs(x, y + 1, cnt + 1);
s[x + 1][y] = '#';
}
}
void solve() {
cin >> n >> m;
ans = inf;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)cin >> s[i][j];
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= m; ++j)cout << s[i][j];
cout << endl;
}
dfs(1, 1, 0);
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while(T--) solve();
return 0;
}
AT Replace
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define int long long
const int N = 3e1 + 5;
#define vi vector<int>
#define vii vector<vi>
#define pii pair<int, int>
int n, to[N], ans;
bool vis[N];//标记可达字母
void dfs(int x){
if(vis[x])return ;
vis[x] = 1;
if(to[x] != -1)dfs(to[x]);
}
void solve(){
cin >> n;
string s, t; cin >> s >> t;
memset(to, -1, sizeof to);
vi in(N); bool f = 0;
for(int i = 0; i < n; ++ i){
int x = s[i] - 'a';
int y = t[i] - 'a';
if(to[x] != -1 && to[x] != y){cout << "-1\n"; return ;}//出现了一个字母指向两个字母
to[x] = y;
}
for(int i = 0; i < 26; ++ i){
if(to[i] != -1){
ans += (to[i] != i);//如果指向其他字母
++in[to[i]];
}
}
for(int i = 0; i < 26; ++i)
if(in[i] == 0 || to[i] == -1)f = 1;//查找空闲字母
for(int i = 0; i < 26; ++i)
if(in[i] == 0 || to[i] == i)dfs(i);//处理链(或点)和自环
for(int i = 0; i < 26; ++i){//剩余的是环
if(!vis[i]){
if(!f){cout << "-1\n"; return ;}
++ans;//只加1是因为前面环中各个节点已经统计过了
dfs(i);
}
}
cout << ans << endl;
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while(T--) solve();
return 0;
}
D - Skibidi Table
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define vi vector<int>
#define vii vector<vi>
#define pii pair<int, int>
inline int cu(int x, int n){//一定要从大到小
if(x == 0)return (1ll << (n - 1));//一次移动坐标增减的情况
if(x == 1)return ((1ll << (2 * n - 2)) * 3);//右上 //先移位再乘
if(x == 2)return ((1ll << (2 * n - 2)) * 2);//左下
if(x == 3)return (1ll << (2 * n - 2));//右下
}
inline int get1(int x, int y, int n){//根据坐标查值 //x,y每次移动对应的值的变化是不同的
if(n == 0)return 1;
if(x > cu(0, n)){//x和y都能减的情况是左下!!
if(y > cu(0, n))return cu(3, n) + get1(x - cu(0, n), y - cu(0, n), n - 1);
else return cu(2, n) + get1(x - cu(0, n), y, n - 1);
}
else if(y > cu(0, n))return cu(1, n) + get1(x, y - cu(0, n), n - 1);
else return get1(x, y, n - 1);
}
inline pii get2(int d, int n){//根据值查坐标
if(n == 0)return {1, 1};
if(d > cu(1, n)){
auto [x,y] = get2(d - cu(1, n), n-1);
return {x, y + cu(0, n)};
}
else if(d > cu(2, n)){
auto [x,y] = get2(d - cu(2, n), n-1);
return {x + cu(0, n), y};
}
else if(d > cu(3, n)){
auto [x,y] = get2(d - cu(3, n), n-1);
return {x + cu(0, n), y + cu(0, n)};
}
else return get2(d, n-1);
}
void solve() {
int n, q; cin >> n >> q;
string s; int x, y, d;
while(q--){
cin >> s;
if(s == "->"){
cin >> x >> y;
cout << get1(x, y, n) << endl;
}
else {
cin >> d;//!!
auto [x0, y0] = get2(d, n);
cout << x0 << " " << y0 << endl;
}
}
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while(T--) solve();
return 0;
}
左儿子右兄弟 - Problem - QOJ.ac
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define vi vector<int>
const int N = 1e5 + 7, mod = 998244353;
vi son[N];
int fac[N], sz[N];
int n;
void dfs(int u){
sz[u] = 1;
for(auto v : son[u]){
dfs(v);
sz[u] += sz[v];//先递归子节点再累加
}
}
void solve(){
cin >> n; fac[0] = 1;
for(int i = 1; i <= n; ++i)fac[i] = i * fac[i - 1] % mod;
for(int fa, i = 2; i <= n; ++i) {
cin >> fa;
son[fa].push_back(i);
}
dfs(1);
int sum = 0, res = 1;
for(int i = 1; i <= n; ++i){
if(son[i].empty())continue;
sort(son[i].begin(), son[i].end(), [&](int x, int y){
return sz[x] > sz[y];
});
int k = son[i].size();
for(int j = 0; j < k; ++j)
sum += (j + 1) * sz[son[i][j]];//贡献,被加了几次
int lsz = sz[son[i][0]];
vi cnt; int count = 1;
for(int j = 1; j < k; ++j){
if(sz[son[i][j]] == lsz) ++count; //有相同的子树可以全排列
else {
cnt.push_back(count);
lsz = sz[son[i][j]];
count = 1;
}
}
cnt.push_back(count);
for(auto c: cnt) res = res * fac[c] % mod;
}
sum += n;
cout << sum << endl << res << endl;
}
signed main(){
ios :: sync_with_stdio(0),cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while(T--) solve();
return 0;
}

浙公网安备 33010602011771号