Wannafly挑战赛21 C、大水题 (DP)

题目链接

题意: 一个数组,元素有两个特征值: id 和 val,问可将左右端点id相同的一段数(不能为1个)截下,可截多段,问截取的总和val可以取得的最大值。


先讲讲我的解题思路: 由于题目是截取一段数,很容易联想到区间dp,例:2……2……2……2 ,这里从第一个2出发,可以不截取,可以选择截取的终点是第2、3、4……个点2,选择的多样性,必然使时间复杂度变为O(n^2)!
区间dp的一个小技巧:将第1个2截取到第2个2,保留第2个2,使得第2个2与第3个2和第4……,实际等价于第1个2截取到第3个2……。时间复杂度降到O(n)

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <set>
#include <map>
#include <vector>
#define llt unsigned long long
using namespace std;

/******************
*** 强势找匹配对222222222222222222222222222,这种匹配对会达到O(n^2)
*** 如果我们去考虑: 后面的2与前面最近的2的匹配,会有问题?
*** 其实我们可以考虑连续相同可以去除
*** 可是去除但也很难用dp处理:12121212121212121212121212
*** 我们讨论:        2……2……2……2……2……2
                     这些东西最多只有1对匹配,如果有两对,合并为一对更大。
*********************/

/*************************
*** 区间dp : 当前点仅需要匹配当前最近的点(不用考虑太多点)

*************************/
const int N = 3e5+777;
struct node {
    int id;//保存输入的编号
    int num;// 位置上的数字
}p[N];
int A[N],Hash[N];
llt sum[N],dp[N][2];
bool cmp(node &a,node &b){return a.num<b.num||(a.num==b.num&&a.id<b.id);}
int n;
llt ddp(int s,int exits){//exits=1,表示已经被前面匹配完成,仅用来看是否前面没有匹配完全
    if(s==n+1) return 0;
    llt &k = dp[s][exits];
    if(k!=-1) return k;
    k = ddp(s+1,0);//此点不匹配
    if(Hash[s]==-1) return k;//不能匹配;
    //能匹配
    k = max(k,ddp(Hash[s],1)+sum[Hash[s]]-(exits?sum[s]:sum[s-1]));
    return k;
}
int main(){
    memset(dp,-1,sizeof(dp));
    scanf("%d",&n);
    for(int i=1;i<=n;++i) {
        scanf("%d",&p[i].num);
        p[i].id = i;
    }
    sort(p+1,p+1+n,cmp);
    for(int i=1;i<n;++i)
        if(p[i].num==p[i+1].num) Hash[p[i].id] = p[i+1].id;//匹配
        else Hash[p[i].id] = -1;
    Hash[p[n].id] = -1;
    sum[0] = 0;
    llt x;
    for(int i=1;i<=n;++i){
        scanf("%llu",&x);
        sum[i] = sum[i-1] + x;
    }
    printf("%llu\n",ddp(1,0));
    return 0;
}

我参考了大佬的代码!被惊到!太美了! 称得上简单dp! 他是遍历的时候将已经遍历的多个id相同的截点的最优val值保留下来了!g[id] = max{ f[i-1] - sum[i-1] | a[i].id = id},实际上只需要和这最优截点相截!只需要一次遍历就能出答案!

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <set>
#include <map>
#include <vector>
#define llt  long long
using namespace std;
const int N = 3e5+777;
const llt INF = 0x7fffffffffffffff;
map <int,llt> Hash;//<x,y> 记录左端点为id的可截取的最优点
llt sum[N];//记录前i个val的和
int id[N];//记录第i个数的特征数
llt f[N];//记录前i个数截取的最大值
//因为有负数-INF,我们不能用 unsigned long long
int main(){
   // cout<<-INF<<endl;
    int n; llt x;
    scanf("%d",&n);
    for(int i=1;i<=n;++i)  scanf("%d",&id[i]);
    sum[0] = 0;
    for(int i=1;i<=n;++i){
        scanf("%lld",&x);
        sum[i] = sum[i-1] + x;
    }

    for(int i=1;i<=n;++i) Hash[id[i]]=-INF;
    f[0] = 0;
    for(int i=1;i<=n;++i){
        f[i] = max(f[i-1],Hash[id[i]]+sum[i]);
        Hash[id[i]] = max(Hash[id[i]],f[i-1]-sum[i-1]);
    }
    printf("%lld\n",f[n]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38786088/article/details/81437639