ACM日常训练日记——7.29(单调栈,滑动窗口,线性dp)

  • Atcoder训练
    1. Enough Array
      高质量题,建议两个星期后重新去做,滑动窗口题,找连续子串的和大于k的数
      我一开始就直接想前缀和去做,但是没有考虑清楚连续的关系,只要到一个状态满足大于它的状态全部都满足
      然后关键的地方是每次找到以后,把最先进入的状态弹出,也就是说从1——k变成2——k的子串,滑动窗口。
      前缀和,加每个区间二分
#include <bits/stdc++.h>
#define Fst first
#define Snd second
#define MP make_pair
#define ll long long
#define PII pair<int,int>
#define PIL pair<int,ll>
#define PLI pair<ll,int>
#define PLL pair<ll,ll>
#define PDD pair<double,double>
using namespace std;
int n;
ll m;
ll a[100010],pre[100010];
int main()
{
    scanf("%d%lld",&n,&m);
    for (int i=0;i<n;i++) scanf("%lld",&a[i]),pre[i+1]=pre[i]+a[i];
    ll res=0;
    for (int i=0;i<n;i++)
    {
        int l=0,r=n-i;
        while (l<r)
        {
            int mid=(l+r)>>1;
            if (pre[i+mid]-pre[i]>=m) r=mid;
            else l=mid+1;
        }
        if (pre[i+l]-pre[i]>=m) res+=n-i-l+1;
    }
    printf("%lld\n",res); return 0;
}

滑动窗口

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

int main() {
	ll n, K;
	cin >> n >> K;
	vector<ll> a(n);
	for (int i = 0; i < n; ++i) {
		cin >> a[i];
	}
	
	ll sum = 0;
	ll ans = 0;
	int left = 0;
	
	for (int i = 0; i < n; ++i) {
		sum += a[i];
		while (sum >= K) {
			ans += n - i; 
			sum -= a[left];
			left++;
		}
	}
	
	cout << ans << endl;
	return 0;
}

  • 动态规划专题
    1. 花店橱窗
      一开始我用dfs做,我觉得数据不大可以试试,但是超时了,只能去用dp去写
      看了别人代码才知道怎么转移,难点在于怎么从上一层转移到下一层,且不能重复走同一个m
动态规划的边界条件:在初始化动态规划表dp时,我们只初始化了前i种花放在前j个花瓶中的状态,
即dp[0][j] = 0,这表示没有花时的美观程度为0。随着循环的进行,我们逐步填充dp[i][j],
其中i表示当前考虑的花的编号,j表示当前考虑的花瓶的编号。

状态转移:在状态转移过程中,我们考虑的是当前第i种花可以放在第j个花瓶中,同时确保第i种花不会放在比它编号小的花瓶中。
这是因为我们在更新dp[i][j]时,总是从dp[i][j-1]开始,即第i种花不放在第j个花瓶中,
然后检查是否将第i种花放在第j个花瓶中比放在第j-1个花瓶中更好(即dp[i-1][j-1] + mp[i][j]是否大于dp[i][j-1])。
这样,我们保证了每种花都至少被放置了一次,并且是按照编号顺序放置的。

路径记录:通过path数组记录了到达每个状态时的选择路径。
在回溯路径时,我们从dp[n][m]开始,
通过比较path[i][j]和j-1的值来确定是直接放置第i种花在第j个花瓶中,还是跳过当前花瓶,继续考虑下一个
#include <bits/stdc++.h>

using namespace std;

int n, m;
int mp[101][101];
int dp[101][101];
int path[101][101]; // 记录路径

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> mp[i][j];
        }
    }

    // 初始化
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            dp[i][j] = INT_MIN;
        }
    }
    for (int j = 0; j <= m; j++) {
        dp[0][j] = 0;
    }

    // 动态规划
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= m; j++) {
            dp[i][j] = dp[i][j-1]; // 不放第i种花在第j个花瓶中
            if (dp[i-1][j-1] + mp[i][j] > dp[i][j]) {
                dp[i][j] = dp[i-1][j-1] + mp[i][j];
                path[i][j] = j; // 记录路径
            }
        }
    }

    // 输出最大美观程度
    cout << dp[n][m] << '\n';

    // 回溯路径
    vector<int> result(n + 1);
    int i = n, j = m;
    while (i > 0 && j > 0) {
        if (path[i][j] == j) {
            result[i] = j;
            i--;
            j--;
        } else {
            j--;
        }
    }

    // 输出每种花应该摆放的花瓶编号
    for (int i = 1; i <= n; i++) {
        cout << result[i] << ' ';
    }
    cout << '\n';

    return 0;
}
posted @ 2024-07-30 19:19  冬天的睡袋  阅读(11)  评论(0编辑  收藏  举报