日常刷题2025-3-2

日常刷题2025-3-2

P1006 [NOIP 2008 提高组] 传纸条

绿色

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

思路:棋盘格DP

从头到尾一来一回其实和从头开始走两次是等效的,我们就当作从头开始走两次思考就好。此题难点在于如何解决重复点的问题。

两条路径不能有重复点,则一定是一条在上面,一条在下面,所以当上面的路径走到 \((i, j)\) 时,下面一条路径只能在 i + 1 行 1 ~ j - 1 列选择。

代码:四维DP

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
#define INF = 0x3f3f3f3f

int a[51][51], f[51][51][51][51];
int m, n;

int max(int i, int j, int k, int l){
    int m = max(i, j), n = max(k, l);
    return max(m, n);
}

int main()
{
    cin >> m >> n;
    for(int i = 1; i <= m; i++){
        for(int j = 1; j <= n; j++)
        {
            cin >> a[i][j];
        }
    }
    f[1][1][1][1] = 0; //garbage
    for(int i = 1; i <= m; i++){
        for(int j = 1; j <= n; j++){
            for(int k = i+1; k <= m; k++){
                for(int l = 1; l < j; l++){
                    f[i][j][k][l] = max(
                        f[i][j-1][k][l-1],
                        f[i][j-1][k-1][l],
                        f[i-1][j][k-1][l],
                        f[i-1][j][k][l-1]
                    )+a[i][j]+a[k][l];
                }
            }
        }
    }
    cout << f[m-1][n][m][n-1] << endl;
    return 0;    
}

代码2:判断重复的写法

为啥只需要把两条路径走到相同点的状态标记成无意义就可以让整个状态转移过程中都不会走到重复的点?

因为DP本质是对重叠子问题用相同的方法反复求解。写DP状态转移时只需要关注一个阶段,就能让所以阶段都按照相同的方式转移。所以把重复点的状态标记成无意义是针对全局所有转移的,这样只要是遇到重复点的状态都不会使用,进而保证了最终答案中就没有用到重复点的状态。

#include <bits/stdc++.h>
using namespace std;
#define int long long
int a[60][60];
int dp[60][60][60][60];
signed main() {
	ios::sync_with_stdio(false);
	ios_base::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int m, n;
	cin >> m >> n;
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= n; j++) {
			cin >> a[i][j];
		}
	}
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= n; j++) {
			for (int k = 1; k <= m; k++) {
				for (int l = 1; l <= n; l++) {
					dp[i][j][k][l] = max(dp[i - 1][j][k - 1][l], dp[i - 1][j][k][l - 1]);
					dp[i][j][k][l] = max(dp[i][j - 1][k - 1][l], max(dp[i][j][k][l], dp[i][j - 1][k][l - 1]));
					dp[i][j][k][l] += a[i][j] + a[k][l];
					if (i == k && j == l && !(i == 1 && j == 1 || i == m && j == n)) {
						dp[i][j][k][l] = -114514;
					}
				}
			}
		}
	}
	cout << dp[m][n][m][n];
	return 0;
}

经验+1

std::string 的读入是按照空格终止的!!!

D - Pigeon Swap

绿色

https://atcoder.jp/contests/abc395/tasks/abc395_d

思路:小巧思

暴力做法肯定不行。

本题的难点就是如何在不用暴力模拟的情况下,又能有办法来表示交换两个巢穴,且保证鸽子的指针不受影响。如果鸽子的指针指向巢的标签,那么在交换巢时难免要去一个个更改鸽子的指针,显然是不可行的。为了避免一个个的修改鸽子的指针,我们就在加一个变量,来表示巢的位置,当发生交换时,不再交换巢的标签,而是交换巢的位置。有了位置后,标签就成了维持鸽子指针不变的中间变量,所以答案肯定不再是这里的标签,而是会因为交换产生变化的位置。

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

void solve(){
	int n, q;
	std::cin >> n >> q;

	std::vector<int> nest(n), pos(n), id(n);
	std::iota(nest.begin(), nest.end(), 0);
	pos = id = nest;
	while (q--){
		int op;
		std::cin >> op;
		if (op == 1){
			int a, b; std::cin >> a >> b;
			a--, b--;
			nest[a] = id[b];
		}else if (op == 2){
			int a, b; std::cin >> a >> b;
			a--, b--;
			pos[id[a]] = b;
			pos[id[b]] = a;
			std::swap(id[a], id[b]);
		}else if (op == 3){
			int a; std::cin >> a;
			a--;
			std::cout << pos[nest[a]] + 1 << '\n';
		}
	}	
}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}
posted @ 2025-03-02 10:22  califeee  阅读(42)  评论(0)    收藏  举报