日常刷题2025-3-1

日常刷题2025-3-1

完美串

111 / 486

https://ac.nowcoder.com/acm/contest/101964/B

思路:构造

此题是一道让相同的字符相隔尽量远的构造,构造时每次选择剩余最多且在 kk 长度以内不冲突的字符构建即可。

构造思路非常值得学习

评述

想到了题目做法但是不会写代码

代码

#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(){
	std::string s;
	std::cin >> s;

	int n = s.size();
	std::vector<pii> p(30);
	std::vector<int> ans;
	std::vector<int> a(26);
	for (auto e : s) a[e-'a']++;

	auto check = [&](int mid)->bool{
		ans.clear();
		std::priority_queue<pii> q;
		for (int i = 0; i < 26; i++) if (a[i]) q.push({a[i], i});
		while (1){
			if (q.size() < mid){
				while (q.size()){
					pii t = q.top();
					q.pop();
					ans.push_back(t.second);
					if (t.first > 1) return 0;
				}
				return 1;
			}
			for (int i = 1; i <= mid; i++){
				pii t = q.top();
				q.pop();
				ans.push_back(t.second);
				p[i] = t;
			}
			for (int i = 1; i <= mid; i++){
				if (p[i].first > 1) q.push({p[i].first-1, p[i].second});
			}
		}
	};


	int l = 1, r = std::min(n ,26);
	while (l < r){
		int mid = (l + r + 1)>>1;
		if (check(mid)) l = mid;
		else r = mid - 1;
	}
	check(l);
	std::cout << l << '\n';
	for (int i = 0; i < ans.size(); i++){
		std::cout << char(ans[i]+'a') ;
	}
	std::cout << '\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;
}

P1004 [NOIP 2000 提高组] 方格取数

绿色

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

思路:四维DP

首先观察到此题的数据范围非常(!)小,所以优先考虑爆搜,爆搜有两种,一种是dfs和bfs,还有一种就是DP。我们选择DP的解法,同时处理两次行程能得到的总的最大值

\(f[i][j][k][l]\):表示第一次走到 i 和 j ,第二次走到 k 和 l 能得到的最大值。

评述

可惜思考的时候没有想到DP,比较傻×了。

代码

#include<cstdio>
#include<iostream>
using namespace std;
const int N=11;
int dp[N][N][N][N];
int a[N][N];
int n,x,y,z;
int main()
{
	scanf("%d",&n);
	for(;;)
	{
		scanf("%d%d%d",&x,&y,&z);
		if(x==y&&y==z&&z==0)
		{
			break;
		}
		else
		{
			a[x][y]=z;
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			for(int k=1;k<=n;k++)
			{
				for(int l=1;l<=n;l++)
				{
					dp[i][j][k][l]=max(max(dp[i-1][j][k-1][l],dp[i-1][j][k][l-1]),max(dp[i][j-1][k-1][l],dp[i][j-1][k][l-1]))+a[i][j]+a[k][l];
					if(i==k&&l==j)dp[i][j][k][l]-=a[i][j];//注意判断这个点走过几次并处理 
				}
			}
		}
	}
	printf("%d",dp[n][n][n][n]);
	return 0;
}

P1005 [NOIP 2007 提高组] 矩阵取数游戏

绿色

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

思路:区间DP

  • n行最大得分和,每一行取数又不会影响到其他行,那么只要确保每一行得分最大,管好自家孩子就行了。(这个在动规中叫最优子结构
  • 每次取数是在边缘取,那么每次取数完剩下来的元素一定是在一个完整的一个区间中,又是求最优解,区间DP应运而生。

评述

想了一下DP结果马上被自己否决了,理由是线性DP都是沿着一个方向,这题一会取左边一会取右边肯定不能用线性DP。但是这不恰好是区间DP的主场吗?人傻了

代码

#include <bits/stdc++.h>

using namespace std;

const int MAXN=81;

inline void input(__int128 &s)
{
    s=0;
    char c=' ';
    while(c>'9'||c<'0') c=getchar();
    while(c>='0'&&c<='9')
    {
        s=s*10+c-'0';
        c=getchar();
    }
}

inline void output(__int128 x)
{
    if(x>9)
        output(x/10);
    putchar(x%10+'0');
}

int n, m;
__int128 game[MAXN][MAXN];

__int128 f[MAXN][MAXN];
__int128 solve(__int128 a[])
{
    memset(f,0,sizeof(f));
    for(int len=0;len<=m;++len)
    	for(int i=1;i+len<=m;++i)
            f[i][i+len]=max(2*f[i+1][i+len]+2*a[i],2*f[i][i+len-1]+2*a[i+len]);
    return f[1][m];
}

__int128 ans=0;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            input(game[i][j]);
    for(int i=1;i<=n;i++)
        ans+=solve(game[i]);
    output(ans);
    return 0;
}
posted @ 2025-03-01 16:07  califeee  阅读(29)  评论(0)    收藏  举报