Loading [MathJax]/jax/element/mml/optable/BasicLatin.js

【序列分段】【区间dp】[UVA12991] Game Rooms

【序列分段】【区间dp】[UVA12991] Game Rooms

一个 N 层的大楼,每层只有一个游戏室,可以设置一个乒乓球桌或游泳池。第 i 层有 Ti 个人喜欢乒乓球和 Pi 个人喜欢游泳。

现在要求使每个人到最近的喜欢的类型的活动室的距离的最小,且这栋大楼要有至少一个乒乓球桌和至少一个游泳池。

思路

考虑到要不乒乓球桌,要不游泳池。所以只有0/1两种情况(很明显不建白不建)

那么我们一整座大楼,从下往上(从第一层开始)就可以表示成一段01串。这些01串有连续的,有单独的。

那么我们也可以把单独的看成长度是1的连续串。

于是乎我们dpi,tpe{0,1}表示强制itpe。那么以i结尾一定会有一个连续都是tpe的串。我们找到这个tpe的极大连续后缀,假设为[k,i],那么很明显首先k1,否则另一种就没地方建了。

然后因为[k,i]是极大联通子串,因此k1i+1一定是tpe1,那么我们就可以从dpk1,tpe1转移过来。

同时[k,i]全部为tpe的代价我们也是可以知道的,假设为cst(k,i,tpe)

由于我们的状态转移已经用去O(n2)了,因此我们的cst只能在O(1)内求出。

因为[k,i]全部是tpe,所以peptpe,[k,i](people)是不需要考虑的了。同时因为懒惰所以mid=(k+i)/2以上的peptpe1都去i+1,以下的都去k1,因此我们也可以得出cst的表达式

cst(k,i,tpe)=ij=kpeptpe1,j×{(jk+1)jmid(i+1j)j>mid

先考虑jmid这一段,他像是一个阶梯式累加的结果1a1+2a2+3a3+不同的就是他不是从1开始的,他是1ak+2ak+1+3ak+2+,那么我们可以定义前缀和bi=ij=1ai,ci=ij=1iai那么就有

1ak+2ak+1+3ak+2++(ik+1)ai=1a1+2a2+(1a1+2a2++(k1)ak1)(k1)(ak+ak+1+ak+2+)=cick1(k1)(bibk1)

然后代入i=mid就可以求出这半段的花费cmidck1(k1)(bmidbk1)

至于后半段,他是一个逆着的,所以我们可以定义di=ij=1bi=ij=1jk=1ak=ij=1(ij+1)aj,这样这个阶梯就是逆着的了。具体推一下

(ik+1)ak++2ai1+1ai=ia1+(i1)a2++1ai(ia1+(i1)a2++(ik+2)ak1)=di[(ik+2)a1+(k2)a1+(ik+2)a2+(k3)a2++(ik+2)ak1]=di(ik+2)(a1+a2++ak1)[(k2)a1+(k3)a2++1ak2]=di(ik+2)bk1dk2

然后代入k=mid+1,可以得到后半段为di(imid+1)bmiddmid1

总结一下,

dpi,tpe=min

注意要开long long,以及特判i=n,k=1两种情况,这两种情况并不能向两边走,只能朝某一头走。

代码中把游戏室的类型放在第一维了。

View Code
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N=4096;
using std::min;
long long int pep[2][N],b[2][N],c[2][N],d[2][N];
long long int dp[2][N];
int T,n;
inline void init()
{
    scanf("%d",&n);
    for(register int i=1;i<=n;++i) scanf("%lld%lld",&pep[0][i],&pep[1][i]);
    memset(dp,0x6f,sizeof(dp));
    for(register long long int i=1;i<=n;++i) b[0][i]=b[0][i-1]+pep[0][i], b[1][i]=b[1][i-1]+pep[1][i];
    for(register long long int i=1;i<=n;++i) c[0][i]=c[0][i-1]+pep[0][i]*i, c[1][i]=c[1][i-1]+pep[1][i]*i;
    for(register long long int i=1;i<=n;++i) d[0][i]=d[0][i-1]+b[0][i], d[1][i]=d[1][i-1]+b[1][i];
    dp[0][0]=dp[1][0]=0;
}
inline long long int cst(int k,int i,int tpe)
{
    tpe^=1;
    if(k==i) return pep[tpe][k];
    if(i==n) return c[tpe][i]-c[tpe][k-1]-(k-1)*(b[tpe][i]-b[tpe][k-1]);
    if(k==1) return d[tpe][i];
    static long long int res=0;
    int mid=(i+k)>>1;
    res=c[tpe][mid]-c[tpe][k-1]-(k-1)*(b[tpe][mid]-b[tpe][k-1]);
    res+=(d[tpe][i]-(i-mid+1)*b[tpe][mid]-d[tpe][mid-1]);
    return res;
}
inline void solve()
{
    static int cases=0;
    init();
    printf("Case #%d: ",++cases);
    for(register int i=1;i<=n;++i)
    {
        for(register int k=i==n?2:1;k<=i;++k)
        {
            dp[0][i]=min(dp[0][i],dp[1][k-1]+cst(k,i,0));
            dp[1][i]=min(dp[1][i],dp[0][k-1]+cst(k,i,1));
        }
    }
    printf("%lld\n",min(dp[0][n],dp[1][n]));
}
int main()
{
    scanf("%d",&T);
    while(T--) solve();
    return 0;
}
posted @   IdanSuce  阅读(78)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个适用于 .NET 的开源整洁架构项目模板
· AI Editor 真的被惊到了
· API 风格选对了,文档写好了,项目就成功了一半!
· 【开源】C#上位机必备高效数据转换助手
· .NET 9.0 使用 Vulkan API 编写跨平台图形应用
点击右上角即可分享
微信分享提示