寒假第二阶段集训第6天

F.Cut the Sequence(POJ 3017)

题意:给你一个长度为n的序列,要求把序列分割成若干个子串,每个子串的数字和不能大于m,每个子串的权值为子串的最大值,求所有子串的最小权值和

思路:dp[i] 维护从1 - i切割序列可以得到的最小权值和

又因为子串的的数字和不能超过m,可以得到转移方程是为 dp[i] = min(dp[k] + max(a[k + 1] , ... , a[i]) , dp[i]);同时k应该满足sum[i] - sum[k] <=m ;

a[k + 1] , ... , a[i] 之间的最大值可以采用单调队列来维护

#include<cstdio>
#include<string.h>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<cctype>
#include<stack>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 100;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-5;
ll arr[maxn];
ll sum[maxn];
ll dp[maxn];
struct node
{
    ll pos;
    ll maxx;
    node(int x, int y)
    {
        pos = x;
        maxx = y;
    }
    node() {}
}que[maxn];

int main()
{
    ll n, m;
    
    while (~scanf("%lld %lld", &n, &m))
    {
        sum[0] = 0;
        dp[0] = 0;
        for (int i = 1; i <= n; ++i) scanf("%lld", &arr[i]);
        for (int i = 1; i <= n; ++i)
        {
            sum[i] = sum[i - 1] + arr[i];
            dp[i] = sum[i];
        }
        int head = 1, rear = 1;
        int l = 1;
        for (ll i = 1; i <= n; ++i)
        {
            if (arr[i] > m)
            {
                printf("-1");
                return 0;
            }
            while (rear > head&& arr[i] > que[rear - 1].maxx) rear--;
            while (sum[i] - sum[l - 1] > m) ++l;
            que[rear++] = node(i, arr[i]);
            while (rear > head&& sum[i] - sum[que[head].pos - 1] > m) head++;
            dp[i] = min(dp[i], dp[l - 1] + que[head].maxx);
            for (int j = head + 1; j < rear; ++j)
            {
                dp[i] = min(dp[i], dp[que[j - 1].pos] + que[j].maxx);
            }
        }
        printf("%lld
", dp[n]);
    }

    return 0;
}
View Code

E.Trade(HDU 3401)

题意:一个人要买股票,一个人每天可以买入ASi支股票,可以卖出BSi支股票,每支股票的买入价格为APi ,卖出价格为BPi,这个人最多可以持有MaxP支股票,询问T天之后,可以收获的最大利益是多少。一开始这个人持有0支股票,但是金钱无限,每隔W天可以进行一次买入或者卖出

思路:dp[i][j] 维护第i天持有j支股票的最大收益

dp[i][j] = max(dp[i - w - 1][k] + (j - k) * AP[i] ,  dp[i - w - 1][k] - (k - j) * AP[i]  , dp[i - w - 1][j] );

转移方程表示在第i天买入股票,卖出股票,既不买入股票也不卖出股票三种情况中去最小

以买入股票为例,卖出股票和买入股票同理

如果第i天买入了股票 dp[i][j] = dp[i - w - 1][k] + (j - k) * AP[i]; 因式分解dp[i][j] + j * AP[i] = dp[i - w - 1][k] + k * AP[i];

因此对于每个i用单调队列维护dp[i - w - 1][k] + k * AP[i]的最大值

同时要求每天买入的股票不超过AS[i]支股票

注:初始化的需要将数组元素初始化为-INF,因为炒股还有有可能会亏钱的,但是最后的结果肯定是大于等于0的,因为如果炒股亏钱的话,那就不要炒股啦。

#include<cstdio>
#include<string.h>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<cctype>
#include<stack>
using namespace std;
typedef long long ll;
const int maxn = 1e4 + 100;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-5;
int ap[maxn], bp[maxn], as[maxn], bs[maxn];
int dp[2020][2020];

struct node
{
    int val;
    int num;
    node(int a, int b)
    {
        val = a;
        num = b;
    }
    node(){}
}que[maxn];

int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        memset(dp, -inf, sizeof dp);
        int n, m,maxx;
        scanf("%d %d %d", &n,&maxx,&m);
        for (int i = 1; i <= n; ++i)
        {
            scanf("%d %d %d %d", &ap[i], &bp[i], &as[i], &bs[i]);
        }
        for (int i = 1; i <= n; ++i)
            for (int j = 0; j <= maxx; ++j)
                dp[i][j] = -inf;
        for (int i = 1; i <= m + 1; ++i)
            for (int j = 0; j <= as[i]; ++j) dp[i][j] = (-ap[i] * j);
        for (int i = 2; i <= n; ++i)
        {
            for(int j = 0 ; j <= maxx ; ++ j)
                dp[i][j] = max(dp[i - 1][j], dp[i][j]);
            if (i <= m + 1) continue;
            // 买入 维护一个单调递减队列
            int rear = 1, head = 1;
            for (int j = 0; j <= maxx; ++j)
            {
                while (rear > head&& que[rear - 1].val < dp[i - m - 1][j] + j * ap[i]) rear--;
                que[rear++] = node(dp[i - m - 1][j] + j * ap[i], j);
                while (rear > head&& que[head].num + as[i] < j) head++;
                dp[i][j] = max(dp[i][j], que[head].val - j * ap[i]);
            }
            rear = 1, head = 1;
            for (int j = maxx; j >= 0; --j)
            {
                while (rear > head&& que[rear - 1].val < dp[i - m - 1][j] + j * bp[i]) rear--;
                que[rear++] = node(dp[i - m - 1][j] + j * bp[i], j);
                while (rear > head&& que[head].num - bs[i] > j) head++;
                dp[i][j] = max(dp[i][j], que[head].val - j * bp[i]);
            }
        }
        int ans = -inf;
        for (int i = 0; i <= maxx; ++i)
        {
            ans = max(ans, dp[n][i]);
        }
        ans = ans < 0 ? 0 : ans;
        cout << ans << endl;
    }
    
    return 0;
}
View Code

 

 

  

原文地址:https://www.cnblogs.com/DreamACMer/p/12291068.html