102400118 林嘉祚 寒假集训第六专题

AC截图:

1、最大子段和
dp[i]表示选到第i个数字时的最大和。状态由当前数字选或不选决定,选则是dp[i-1]+a[i],不选则是a[i],取max即可。

#include <iostream>
using namespace std;

int arr[200010];
int dp[200010];

int main()
{
	int n;
	cin >> n;
	for (int i = 1 ; i <= n ; i++)
	{
		scanf("%d",&arr[i]);
	}
	
	for (int i = 1 ; i <= n ; i++) //头 
	{
		dp[i] = max(dp[i-1]+arr[i],arr[i]);
	}
	
	int maxn = -1e9;
	for (int i = 1 ; i <= n ; i++)
	{
		maxn = max(maxn,dp[i]);
	}
	
	cout << maxn << endl;
}

2、采药
经典的01背包问题,状态同样由选和不选转移

#include <iostream>
using namespace std;

int f[110][1010];

int main()
{
	int t[110];
	int w[110];
	int T,m;
	cin >> T >> m;
	for (int i = 1 ; i <= m ; i++)
	{
		cin >> t[i] >> w[i];
	}
	
	for (int i = 1 ; i <= m ; i++)
	{
		for (int j = 0 ; j <= T ; j++)
		{
			if (j < t[i])
			{
				f[i][j] = f[i-1][j];
			}
			else
			{
				f[i][j] = max(f[i-1][j],f[i-1][j-t[i]]+w[i]);
			}
		}
	}
	
	cout << f[m][T] << endl;
	
	return 0;
}

3、宝物筛选
多重背包模版题,用二进制优化枚举过程

#include <iostream>
using namespace std;

const int N = 1e5 + 10;
const int M = 4e4 + 10;
long long dp[M];
int v[N]; //价值 
int w[N]; //重量 

int main()
{
	int n,W;
	cin >> n >> W;
	
	int cnt = 0;
	for (int i = 1 ; i <= n ; i++)
	{
		int v1,w1,m1;
		cin >> v1 >> w1 >> m1;
		int t = 1;
		while (m1 >= t)
		{
			cnt++;
		 	v[cnt] = v1 * t;
		 	w[cnt] = w1 * t;
		 	m1 -= t;
		 	t <<= 1;
		}
		
		if (m1 > 0)
		{
			cnt++;
			v[cnt] = v1 * m1;
			w[cnt] = w1 * m1;
		}
	}
	
	for (int i = 1 ; i <= cnt ; i++) //枚举物品
	{
		for (int j = W ; j >= 0 ; j--)
		{
			if (j >= w[i])
			{
				dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
			}
		} 
	}
	
	cout << dp[W] << endl;
	
	return 0; 
}

4、最长公共子序列
朴素的求LCS做法O(n^2)一定超时,考虑优化。求LCS问题可以转化成在1个数组中求LIS。首先将a数组元素顺序标号,同时将这些标号映射到b数组中,问题就转化成了在这个映射后的新数组中求LIS。

#include <iostream>
using namespace std;

const int N = 1e5 + 10;
int a[N],a1[N];
int b[N];
int dp[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int n;
    cin >> n;
    for (int i = 1 ; i <= n ; i++)
    {
        cin >> a[i];
        a1[a[i]] = i;
    }
    for (int i = 1 ; i <= n ; i++) cin >> b[i];

    int len = 0;
    for (int i = 1 ; i <= n ; i++)
    {
        int l = 0;
        int r = len + 1;
        while (l+1 != r)
        {
            int mid = (l+r) >> 1;
            if (a1[b[i]] > dp[mid]) l = mid;
            else r = mid;
        }

        if (r > len)
        {
            len++;
            dp[len] = a1[b[i]];
        }
        else dp[r] = min(dp[r],a1[b[i]]);
    }

    cout << len << endl;

    return 0;
}

5、Kevin and Puzzle
初见时并不会做,听完讲解才补完。
本题关键在于两个说谎的人不会站在一起。所以i位置为骗子时,只可能由i-1位置为真话转移来。难点在于i位置为真话时,考虑由i-1位置为真话或为假话转移,讨论转移满足的条件。若i-1位置为真话时,a[i]必须与a[i-1]相等;若i-1位置为假话时,则i-2必定是真话,此时需要满足a[i]=a[i-2]+1。

#include <iostream>
#include <cstring>
using namespace std;

const int MOD = 998244353;
const int N = 2e5 + 10;
int a[N];
int dp[N][2]; //0诚实,1说谎

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int t;
    cin >> t;
    while (t--)
    {
        int n;
        cin >> n;
        for (int i = 1 ; i <= n ; i++) cin >> a[i];
        
        memset(dp,0,sizeof(dp));
        dp[0][0] = 1;
        for (int i = 1 ; i <= n ; i++)
        {
            dp[i][1] = dp[i-1][0];
            if (a[i] == a[i-1])
            {
                dp[i][0] += dp[i-1][0];
                dp[i][0] %= MOD;
            }
            if (i-2 >= 0 && a[i-2]+1 == a[i])
            {
                dp[i][0] += dp[i-2][0];
                dp[i][0] %= MOD;
            }
        }

        cout << (dp[n][0]+dp[n][1]) % MOD << endl;
    }

    return 0;
}

6、World is Mine
本题拿到首先考虑二人都会以什么策略吃蛋糕。显然,Alice一定是从还存有的比上一个吃的大的蛋糕里,找到最小的那个。至于Bob,一开始可能认为Bob吃数量最少的蛋糕是最优解。但是由于吃蛋糕具有时效性,因此这种贪心做法并不正确,只能使用dp。
记dp[i][j]为前i个蛋糕,Bob吃了j个,Bob能消除的蛋糕种数。Bob吃掉一种蛋糕的代价为该蛋糕个数+1,因为若消除了此种蛋糕,Bob会在吃到i时失去一次吃蛋糕机会。于是问题就转化成了背包dp

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 5010;
int a[N];
int cnt[N];
int dp[N][N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int t;
    cin >> t;
    while (t--)
    {
        int n;
        cin >> n;
        memset(cnt,0,sizeof(cnt));
        for (int i = 1 ; i <= n ; i++)
        {
            cin >> a[i];
            cnt[a[i]]++;
        }

        sort(a+1,a+n+1);
        int end = unique(a+1,a+n+1) - a - 1;
        for (int i = 1 ; i <= end ; i++)
        {
            for (int j = 1 ; j <= i ; j++)
            {
                dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
                if (j >= cnt[a[i]]+1)
                {
                    dp[i][j] = max(dp[i][j],dp[i-1][j-cnt[a[i]]-1]+1);
                }
                // cout << dp[i][j] << " ";
            }
            // cout << endl;
        }

        cout << end - dp[end][end] << endl;
    }

    return 0;
}
posted @   梦里花落人悲  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示