dp/贪心 (中石油组队赛-JTiling Terrace)

在这里插入图片描述
题意:给你一个只包含".“和”#"的序列,你需要覆盖这个序列,有三种覆盖方式:

覆盖 “.” 价值为G1
覆盖"..“价值为G2
覆盖”.#."价值为G3

每个点只能被覆盖一遍,但是不必覆盖所有的点,第一中覆盖方式最多只能有K个。为最大化覆盖价值和。最多50个”#“号

显然是个dp,dp[i][j][2]为到达第i个井号,用了j个第一覆盖,最后一个井号有没有被覆盖。pos[i]为第i个井号的位置转移方程为:
s u m = p o s [ i ] − p o s [ i − 1 ] − 1 sum = pos[i]-pos[i-1]-1 sum=pos[i]pos[i1]1

d p [ i ] [ j ] [ 0 ] = max ⁡ 1 < = c < = m i n ( k , s u m ) { d p [ i − 1 ] [ c ] [ 0 ] + G 1 ∗ ( s u m − c ) + G 2 ∗ ⌊ c 2 ⌋ } dp[i][j][0]=\max_{1<=c<=min(k, sum)}\{dp[i-1][c][0] + G1*(sum-c)+G2* \lfloor \frac{c}{2}\rfloor \} dp[i][j][0]=1<=c<=min(k,sum)max{dp[i1][c][0]+G1(sumc)+G22c}

d p [ i ] [ j ] [ 0 ] = max ⁡ 1 < = c < = m i n ( k , s u m − 1 ) { d p [ i − 1 ] [ c ] [ 1 ] + G 1 ∗ ( s u m − 1 − c ) + G 2 ∗ ⌊ c 2 ⌋ } dp[i][j][0]=\max_{1<=c<=min(k, sum-1)}\{dp[i-1][c][1] + G1*(sum-1-c)+G2* \lfloor \frac{c}{2}\rfloor \} dp[i][j][0]=1<=c<=min(k,sum1)max{dp[i1][c][1]+G1(sum1c)+G22c}

d p [ i ] [ j ] [ 1 ] = max ⁡ 1 < = c < = m i n ( k , s u m − 1 ) { d p [ i − 1 ] [ c ] [ 0 ] + G 1 ∗ ( s u m − 1 − c ) + G 2 ∗ ⌊ c 2 ⌋ + G 3 } dp[i][j][1]=\max_{1<=c<=min(k, sum-1)}\{dp[i-1][c][0] + G1*(sum-1-c)+G2* \lfloor \frac{c}{2}\rfloor +G3\} dp[i][j][1]=1<=c<=min(k,sum1)max{dp[i1][c][0]+G1(sum1c)+G22c+G3}

d p [ i ] [ j ] [ 1 ] = max ⁡ 1 < = c < = m i n ( k , s u m − 2 ) { d p [ i − 1 ] [ c ] [ 1 ] + G 1 ∗ ( s u m − 2 − c ) + G 2 ∗ ⌊ c 2 ⌋ + G 3 } dp[i][j][1]=\max_{1<=c<=min(k, sum-2)}\{dp[i-1][c][1] + G1*(sum-2-c)+G2* \lfloor \frac{c}{2}\rfloor +G3\} dp[i][j][1]=1<=c<=min(k,sum2)max{dp[i1][c][1]+G1(sum2c)+G22c+G3}
很遗憾,因为式子中有个向下取值,我们无法通过提取c,然后求前缀最大值进行优化(最后时间复杂度为 O ( c n t ∗ K 2 ) O(cnt*K^2) O(cntK2)cnt为井号的个数)。这时考虑贪心,显然,G1和G2可以有一个贪心。我们得知,对于题目情况,显然是要么尽量选G1,要么尽量选G2。我们不妨在dp的时候尽量选G2,然后最后考虑用G1替换G2。
这时有dp[i][j][2][k] 为考虑到第i个井号,一个覆盖了j个井号,最后一个井号有没有覆盖。有k个’.'没有被覆盖。的情况。为啥要这么记:因为这样我们就可以针对每一个情况,得知没有被覆盖的数量,和算出被G2覆盖的数量。方便最后的贪心。其中G2覆盖的数量sumg(n为序列总长,cnt为井号数量):
s u m g = n − 3 ∗ j − k − ( c n t − j ) sumg=n-3*j-k-(cnt-j) sumg=n3jk(cntj)
转移方程为:
s u m = p o s [ i ] − p o s [ i − 1 ] − 1 sum = pos[i]-pos[i-1]-1 sum=pos[i]pos[i1]1

d p [ i ] [ j ] [ 0 ] [ k + ( s u m   m o d   2 ) ] = max ⁡ 1 < = k < = c n t { d p [ i ] [ j ] [ 0 ] [ k ] + G 2 ∗ ⌊ s u m 2 ⌋ } dp[i][j][0][k+(sum\ mod\ 2)]=\max_{1<=k<=cnt}\{ dp[i][j][0][k]+G2*\lfloor \frac{sum}{2}\rfloor\} dp[i][j][0][k+(sum mod 2)]=1<=k<=cntmax{dp[i][j][0][k]+G22sum}

d p [ i ] [ j ] [ 0 ] [ k + ( s u m − 1   m o d   2 ) ] = max ⁡ 1 < = k < = c n t { d p [ i ] [ j ] [ 1 ] [ k ] + G 2 ∗ ⌊ s u m − 1 2 ⌋ } dp[i][j][0][k+(sum-1\ mod\ 2)]=\max_{1<=k<=cnt}\{ dp[i][j][1][k]+G2*\lfloor \frac{sum-1}{2}\rfloor\} dp[i][j][0][k+(sum1 mod 2)]=1<=k<=cntmax{dp[i][j][1][k]+G22sum1}

d p [ i ] [ j ] [ 1 ] [ k + ( s u m − 1   m o d   2 ) ] = max ⁡ 1 < = k < = c n t { d p [ i ] [ j − 1 ] [ 0 ] [ k ] + G 2 ∗ ⌊ s u m − 1 2 ⌋ + G 3 } dp[i][j][1][k+(sum-1\ mod\ 2)]=\max_{1<=k<=cnt}\{ dp[i][j-1][0][k]+G2*\lfloor \frac{sum-1}{2}\rfloor+G3\} dp[i][j][1][k+(sum1 mod 2)]=1<=k<=cntmax{dp[i][j1][0][k]+G22sum1+G3}

d p [ i ] [ j ] [ 1 ] [ k + ( s u m − 2   m o d   2 ) ] = max ⁡ 1 < = k < = c n t { d p [ i ] [ j − 1 ] [ 1 ] [ k ] + G 2 ∗ ⌊ s u m − 2 2 ⌋ + G 3 } dp[i][j][1][k+(sum-2\ mod\ 2)]=\max_{1<=k<=cnt}\{ dp[i][j-1][1][k]+G2*\lfloor \frac{sum-2}{2}\rfloor+G3\} dp[i][j][1][k+(sum2 mod 2)]=1<=k<=cntmax{dp[i][j1][1][k]+G22sum2+G3}

sum%2,即看该区间内的"."的数量是不是奇数,如果是奇数,既有一个空位,否则没有。
此时dp部分得以解决。时间复杂度为 O ( c n t 3 ) O(cnt^3) O(cnt3)

我们在序列的最后加一个井号,最后答案在dp[cnt][p][0][c](0<=c<=cnt, 0<=p<=cnt)中求解产生,我们贪心的都取G1或者G2。
a n s 1 = max ⁡ 0 < = c < = c n t , 0 < = p < = c n t { d p [ c n t ] [ p ] [ 0 ] [ c ] + G 1 ∗ m i n ( c , K ) + G 1 ∗ v a l − G 2 ∗ ⌈ v a l 2 ⌉ } ans1=\max_{0<=c<=cnt, 0<=p<=cnt}\{ dp[cnt][p][0][c]+G1*min(c,K)+G1*val-G2*\lceil\frac{val}{2}\rceil\} ans1=0<=c<=cnt,0<=p<=cntmax{dp[cnt][p][0][c]+G1min(c,K)+G1valG22val}
如果min(la, sump)为奇数,答案还有:
a n s 2 = max ⁡ 0 < = c < = c n t , 0 < = p < = c n t { d p [ c n t ] [ p ] [ 0 ] [ c ] + G 1 ∗ m i n ( c , K ) + G 1 ∗ ( v a l − 1 ) − G 2 ∗ ⌈ v a l − 1 2 ⌉ } ans2=\max_{0<=c<=cnt, 0<=p<=cnt}\{ dp[cnt][p][0][c]+G1*min(c,K)+G1*(val-1)-G2*\lceil\frac{val-1}{2}\rceil\} ans2=0<=c<=cnt,0<=p<=cntmax{dp[cnt][p][0][c]+G1min(c,K)+G1(val1)G22val1}
最后答案为:
a n s = m a x ( a n s 1 , a n s 2 , max ⁡ 0 < = c < = c n t , 0 < = p < = c n t { d p [ c n t ] [ p ] [ 0 ] [ c ] + G 1 ∗ m i n ( c , K ) } ) ans = max(ans1, ans2,\max_{0<=c<=cnt, 0<=p<=cnt}\{ dp[cnt][p][0][c]+G1*min(c,K)\}) ans=max(ans1,ans2,0<=c<=cnt,0<=p<=cntmax{dp[cnt][p][0][c]+G1min(c,K)})
其中: l a = k − m i n ( c , K ) , v a l = m i n ( l a , s u m p ) la=k - min(c, K), val=min(la, sump) la=kmin(c,K),val=min(la,sump)
之所有有ans2,是因为,我们如果将奇数个"."替换为G1,就会导致出现一个空位无法用G2覆盖,无法判定是不是更优的,(当然可以直接通过判G1,G2的大小得知,但是我懒得判了)
所有找解的复杂度为 O ( c n t 2 ) O(cnt^2) O(cnt2),最终复杂度为 O ( c n t 3 ) O(cnt^3) O(cnt3)

下面是ac代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include <map>
#include <queue>
#include <set>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <bitset>
#include <array>
#include <cctype>
#include <time.h>
 
#pragma GCC optimize(2)
 
void read_f() { freopen("1.in", "r", stdin); freopen("1.out", "w", stdout);}
void fast_cin() { std::ios::sync_with_stdio(false); std::cin.tie(); }
void run_time() { std::cout << "ESC in : " << clock() * 1000.0 / CLOCKS_PER_SEC << "ms" << std::endl; }
 
#define ll long long
#define ull unsigned ll
#define _min(x, y) ((x)>(y)?(y):(x))
#define _max(x, y) ((x)>(y)?(x):(y))
#define max3(x, y, z) ( max( (x), max( (y), (z) ) ) )
#define min3(x, y, z) ( min( (x), min( (y), (z) ) ) )
#define pr(x, y) (make_pair((x), (y)))
#define pb(x) push_back(x);
using namespace std;
  
const int N = 1e5+5;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
 
int pos[N], cnt;
ll dp[64][64][2][128];
char su[N];
int main()
{
    ll n, k, g1, g2, g3;
    scanf("%lld%lld%lld%lld%lld", &n, &k, &g1, &g2, &g3);
    memset(dp, 0x80808080, sizeof(dp));
    scanf("%s", su+1);
    for (int i = 1; i <= n; i++)
        if (su[i] == '#') pos[++cnt] = i; 
    pos[++cnt] = n + 1;
    dp[0][0][0][0] = 0;
    for (int i = 1; i <= cnt; i++)
    {
        ll c = pos[i] - pos[i-1] - 1;
       // cout << c << endl;
        for (int j = 0; j <= cnt; j++)
        {
            for (int k = 0; k <= cnt; k++)
            {
                dp[i][j][0][k + (c & 1)] = max( dp[i][j][0][k + (c & 1)] , dp[i-1][j][0][k] + (c >> 1) * g2); 
                if (c >= 1)
                {
                    dp[i][j][0][k + ((c-1) & 1)] = max( dp[i][j][0][k + ((c-1) & 1)] , dp[i-1][j][1][k] + ((c-1) >> 1) * g2); 
                    if (j >= 1
                        dp[i][j][1][k + ((c-1) & 1)] = max( dp[i][j][1][k + ((c-1) & 1)] , dp[i-1][j-1][0][k] + ((c-1) >> 1) * g2 + g3);
                }
                if (c >= 2 && j >= 1)
                {
                    dp[i][j][1][k + ((c-2) & 1)] = max( dp[i][j][1][k + ((c-2) & 1)] , dp[i-1][j-1][1][k] + ((c-2) >> 1) * g2 + g3); 
                }
            }
        }
    }
    ll ans = 0;
    for (int p= 0; p <= cnt; p++)
    {
        for (int c = 0; c <= cnt; c++)
        {
            ll val = dp[cnt][p][0][c];
            val += min(1ll * c, k) * g1;
            ll la = k - min(1ll * c, k);
            if (la && n - p*3 - c - (cnt - 1 - p) >= 0)
            {
                ll po = n - p*3 - c -  (cnt - 1 - p);
                val = max3(val, val + min(la, po) * g1 - (min(la, po) + 1) / 2 * g2, val + (min(la, po)&~1) * g1 - ((min(la, po)&~1) + 1) / 2 * g2 );
            }
            ans = max(ans, val);
        }
    }
    printf("%lld\n", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值