5月20号dp专题机房模拟赛(我并没有什么可以给你,真愧怍)

题解显然显然,都tm是显然,题解都是显然,怀疑人生


这里写图片描述


先说第二题吧

Problem 2 (string.cpp/c/pas)

【题目描述】
有一个的字符串S需要拆分成k个串,每一个串需要花费一些代价来维护。对于一个串,其维护的代价为第i个字符在模式串P中的位置pos的(i – pos) * pos之和。现在需要计算出k个串的最小维护代价。
【输入格式】
第一行一个字符串P和一个整数k。
第二行一个字符串S。
【输出格式】
一行,最小维护代价。
【样例输入】
abcdefghijklmnopqrstuvwzyx 3
aabbbccccccccc
【样例输出】、
2
【数据范围】
对于30%的数据 k <= 100,|S| <= 100;
对于100%的数据 1 <= k <= 500,1 <= |S| <= 20000, 模式串P中不存在相同的两个的字符,所有字符都是小写字母。
【样例说明】
拆成“aabbb”,“cccc”和“ccccc”,6=(0 + 0 + 1 + 2 + 3), -4=(-4 -2 + 0 + 2) 和 0 = (-4 -2 + 0 + 2 + 4)。


题解:

string题解
用dp[i][j]表示前i个字符分成j段的最小代价和
得到转移方程
dp[i][j] = dp[k][j - 1] + (0 * pos[k + 1] + 1 * pos[k + 2] + … + (i - k - 1) * pos[i]) - (pos[k + 1] ^ 2 + pos[k + 2] ^ 2 + … + pos[i] ^ 2)
=dp[k][j - 1] + (k * pos[k + 1] + (k + 1) * pos[k + 2] + … + (i - 1) * pos[i] ) -k * (pos[k + 1] + pos[k + 2] + … + pos[i]) - (pos[k + 1] ^ 2 + pos[k + 2] ^ 2 + … + pos[i] ^ 2)

很长的一个式子,接下来的问题就是处理括号里的那些数据了
用s1表示 (pos[k + 1] + pos[k + 2] + … + pos[i])的前缀和
用s2表示 (k * pos[k + 1] + (k + 1) * pos[k + 2] + … + (i - 1) * pos[i] )的前缀和
用s3表示(pos[k + 1] ^ 2 + pos[k + 2] ^ 2 +… + pos[i] ^ 2)的前缀和
则dp方程表示如下
dp[i][j] = dp[k][j - 1] + (s2[i] - s2[k]) - k * (s1[i] - s1[k]) - (s3[i] - s3[k])
设l > k,且点l比点k优
则dp[k][j - 1] + (s2[i] - s2[k]) - k * (s1[i] - s1[k]) - (s3[i] - s3[k]) >= dp[l][j - 1] + (s2[i] - s2[l]) - l * (s1[i] - s1[l]) - (s3[i] - s3[l])
化简得s1[i] >= ( (dp[l][j - 1] - s2[l] + s3[l] + l * s2[l]) - (dp[k][j - 1] - s2[k] + s3[k] + k * s2[k]) ) / (l - k)
因为s1随着i的递增而递增,所以得到斜率方程

最开始我做这道题的时候,是用的四边形优化,考试的时候打了几个表,如下

for(int i=1;i<n;i++){
    for(int j=i;j<n;j++){
        if(cost[i][j]+cost[i+1][j+1]<=cost[i+1][j]+cost[i][j+1]){
            cout<<"true"<<endl;
        }else{
            cout<<"false"<<endl;
        }
    }
}

结果是这样的

true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true

--------------------------------
Process exited with return value 0
Press any key to continue . . .

w是满足四边形的,决策也是单调的

//满足四边形不等式且满足包含原则
//cost[i+1][j]一定小于等于cost[i][j+1] 
//for(char i='a';i!='z';i++){
//  printf("%d",m[i]);
//}
//for(int i=1;i<=n;i++){
//  cout<<cnt[i]<<endl;
//}cout<<"dp[1]["<<i<<"]::"<<dp[1][i]<<endl;
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true
true

--------------------------------
Process exited with return value 0
Press any key to continue . . .

显然

可以用四边形优化,然而这道题卡常,只过了三十分

如下是代码,代码没错,不卡应该过的

#include<cstdio>
#include<iostream>
#include<cstring>
#define MAX_K 500+10
#define MAXN 20000+10
#include<map>
using namespace std;

char temp[MAXN];
map<char,int>m;
int k,n;
long long cnt[MAXN];
long long dp[MAX_K][MAXN];
int s[MAX_K][MAXN];
long long a[MAXN],b[MAXN];

void init(){
    scanf("%s",temp);
    scanf("%d",&k);
    n=strlen(temp); 
    for(register int i=0;i<n;i++){
        m[temp[i]]=i;
    }
    scanf("%s",temp);
    n=strlen(temp);
    for(register int i=0;i<n;i++) cnt[i+1]=cnt[i]+(m[temp[i]])*(m[temp[i]]);
    for(register int i=0;i<n;i++) a[i+1]=a[i]+m[temp[i]];
    for(register int i=0;i<n;i++) b[i+1]=b[i]+i*m[temp[i]];
}

long long getvalue(int from,int to){
    long long te=0;
    te=b[to]-b[from-1]-(a[to]-a[from-1])*(from-1);
    te-=cnt[to]-cnt[from-1];
    return te;
}

void dpp(){
    for(register int i=1;i<=n;i++){
        dp[1][i]=getvalue(1,i);
        //s[1][i]=i-1;
    }
    for(register int i=2;i<=k;i++){
        s[i][n+1]=n;
        for(register int j=n;j>=i;j--){
            long long te=0x7fffffff;
            int mp;
            for(register int kk=s[i-1][j];kk<=s[i][j+1];kk++){
                long long tem=getvalue(kk+1,j);
                if(te>dp[i-1][kk]+tem){
                    te=dp[i-1][kk]+tem;
                    mp=kk;
                }
            }
            dp[i][j]=te;
            s[i][j]=mp;
        }
    }
    cout<<dp[k][n]<<endl;
}

int main(){
    freopen("string.in","r",stdin);
    freopen("string.out","w",stdout);
    init();
    dpp();
    return 0;
}

现在敲斜率的代码,垃圾斜率

如下是垃圾斜率优化的代码

过了

#include<cstdio>
#include<iostream>
#include<cstring>
#define MAX_K 500+10
#define MAXN 20000+10
#include<map>
using namespace std;

char temp[MAXN];
map<char,int>m;
int k,n;
long long cnt[MAXN];
long long dp[MAX_K][MAXN];
int s[MAX_K][MAXN];
long long a[MAXN],b[MAXN];
int head,tail;
int q[MAXN];

inline long long gety(int i,int k,int h){ return dp[i-1][k]-b[k]+a[k]*k+cnt[k]-(dp[i-1][h]-b[h]+a[h]*h+cnt[h]); }

void init(){
    scanf("%s",temp);
    scanf("%d",&k);
    n=strlen(temp); 
    for(register int i=0;i<n;i++){
        m[temp[i]]=i;
    }
    scanf("%s",temp);
    n=strlen(temp);
    for(register int i=0;i<n;i++) cnt[i+1]=cnt[i]+(m[temp[i]])*(m[temp[i]]);
    for(register int i=0;i<n;i++) a[i+1]=a[i]+m[temp[i]];
    for(register int i=0;i<n;i++) b[i+1]=b[i]+i*m[temp[i]];
}

long long getvalue(int from,int to){
    long long te=0;
    te=b[to]-b[from-1]-(a[to]-a[from-1])*(from-1);
    te-=cnt[to]-cnt[from-1];
    return te;
}

void dpp(){
    for(register int i=1;i<=n;i++){
        dp[1][i]=getvalue(1,i);
    }
    for(register int i=2;i<=k;i++){
        head=tail=0;
        q[tail++]=i-1;
        for(register int j=i;j<=n;j++){
            while(head+1<tail&&gety(i,q[head+1],q[head])<=a[j]*(q[head+1]-q[head]))head++;
            dp[i][j]=dp[i-1][q[head]]+getvalue(q[head]+1,j);
            while(head+1<tail&&gety(i,j,q[tail-1])*(q[tail-1]-q[tail-2])<=gety(i,q[tail-1],q[tail-2])*(j-q[tail-1]))tail--;
            q[tail++]=j;
        }
    }
    cout<<dp[k][n]<<endl;
}

int main(){
    freopen("string.in","r",stdin);
    freopen("string.out","w",stdout);
    init();
    dpp();
    return 0;
}

再来看第三题?

Problem 3 (deliver.pas/cpp/c)

【题目描述】
有一个军队正在编队。他们有n个人,编号1-n,编队结构如下:
1.一个军队有一个指挥官
2.编号小于他们指挥官编号的组成一个军队,编号大于他们指挥官编号的组成一个军队,这两个军队都可以是空集
3.非空子集有他们自己的编队结构
4.两个子军队的指挥官是这个军队的副指挥
这个编队用来传达一个消息:消息会传达个一个士兵,然后他传给他的直接指挥,一直到消息传达到整个团队的指挥为止。传递时间是传递过程中所有人的读取信息时间总和。
不幸的是不知道那个人将会受到消息,因此我们要使最大传递时间最小。
【输入格式】
输入一组数据,第一行有一个整数n表示士兵的人数
接下来一行有n个整数ai表示编号为i的士兵的读取信息时间
【输出格式】
输出一个整数表示最小可能的最大传递时间
【样例输入】
5
6 2 4 7 4
【样例输出】
13
【数据范围】
对于50%的数据 n <= 500;
对于100%的数据 1 <= n <= 2000,1 <= ai <= 10^9。
【样例说明】
{[6]2[(4)7(4)]}


题解:

deliver题解
显然我们可以写出一个区间dp[l,r]表示[l:r)的区间:
dp[l,r]=min{ai+max(dp[l,i],dp[i+1,r])},l<=i< r
我们想用决策单调性优化,当显然不行。

显然dp[l,r],l不变,r变大时dp值变大;r不变,l变小时dp值变大。
所以,我们能对于任意的(l,r),l< r,找到一个k:
dp[l,j]<=dp[j+1,r],l<=j<=k
dp[l,j]>dp[j+1,r],k< j<=r

如果我们得到区间内所有的dp值,我们可以用二分log找到k
那么我们的dp式变为:
dp[l,r]=min{min{l<=j<=k}(aj+dp[j+1,r]),min{k< j<=r}(aj+dp[l,j])}
用二维线段树或二维树状数组维护一下minl(x,y),minr(x,y)就行
其表示min{ai+dp[l,i]},x<=i< y ; min{ai+dp[i,r]}, x<=i< y

最终复杂度n^2logn

很显然,树套树(二维segment,二维fenwick),我都不会,那还是暴力吧,伤心
暴力思维就是题解所给的,区间dp爆搞,可以搞五十分,性价比还是可以的,主要是代码精简,如果不加读优,不到三十行

#include<cstdio>
#include<iostream>
#include<cstring>
#define MAXN 2000+10
#define LL long long
using namespace std;
LL dp[MAXN][MAXN],a[MAXN],n;
int main(){
    freopen("deliver.in","r",stdin);
    freopen("deliver.out","w",stdout);
    scanf("%d",&n);
    memset(dp, 0x3f, sizeof dp);
    for(register int i=1;i<=n;i++) scanf("%d",&a[i]),dp[i][i]=a[i];
    for(register int len=1;len<n;len++){
        for(register int i=1;i<=n;i++){
            int j=i+len;
            if(j>n) continue;
            for(register int k=i;k<=j;k++){
                if(k-1 < i) dp[i][k-1] = 0;
                if(j < k+1) dp[k+1][j] = 0;
                dp[i][j]=min(dp[i][j],max(dp[i][k-1],dp[k+1][j])+a[k]);
            }
        }
    }
    cout<<dp[1][n]<<endl;
    return 0;
}

没办法,开了5秒时限也没过


然后让我们来看第一题

Problem 1 (treasure.cpp/c/pas)

【题目描述】
大博物馆刚刚宣布了来自世界各地的珠宝的大型展览。 在里面希望他潜在的未来繁荣,世界着名的小偷和主犯罪爱德华Terrenando已经决定尝试他的职业生涯中的巨大的恶作剧。
爱德华希望从大型博物馆的展览中兜售大量的珠宝。 但唉! 他必须小心地偷适当的珠宝,以最大化的总价值。爱德华有k个背包,大小为1,2,3到k,并且想知道每个背包可窃取珠宝的价值最大值。这样,他可以适当地权衡风险与报酬选择多少珠宝偷。 尺寸s的背包可以容纳尺寸的总和小于或等于s的物品。 如果你能找出每个背包的最佳总价值,你可以帮助爱德华拉开世纪的犯罪史!
【输入格式】
输入一组数据,第一行有两个整数n,k分别表示珠宝的数量和他最大的背包的值。
接下来n行,每行分别描述一个珠宝。每行有两个整数s,v分别表示珠宝的尺寸和价值。
各个背包之间的问题是相互独立的。
【输出格式】
输出一行k个整数,用空格分隔。表示背包大小为1到k的背包分别最多带多少价值的的珠宝。
【样例输入】
4 9
2 8
1 1
3 4
5 100
【样例输出】
1 8 9 9 100 101 108 109 109
【数据范围】
对于50%的数据,k <= 1000;
对于100%的数据,1 <= n <= 10^6, 1 <= k <= 40000, 1 <= s <= 300, 1 <= v <= 10^9。


这是第一道题,根本不知道翻译的什么,学长扒题的时候估计直接百度或者谷歌翻了就给我们了


题解:

treasure题解
显然是背包
但看一下尺寸限制<=300,显然这是突破口。
对于每个尺寸s分别分析:
我们对尺寸是s的珠宝一起计算,先把背包分为mod s下的s份,这些珠宝只会一起影响一份,对于每一份来说这些珠宝就是尺寸为1。
那么我们的dp式变成了:
dp[x]=max(dp0[i]+w[x-i])
dp0[x]表示用1~s-1的珠宝当前背包大小为x最多获得的价值
dp[x]表示用1~s的珠宝当前背包大小为x最多获得的价值
w[x]是把尺寸为s的珠宝按价值从大到小排序后的前缀和
观察这个dp式,dp0和w显然是凸函数,我们可以用单调性优化。

显然

这道题做不来

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值