区间DP

另一个专题。

T1 YATO

将栈的状态作为一棵树来看。
那么每次弹出的操作就是对于当前最左侧的这个元素来说的。
枚举其弹出的时间。
前缀和处理在其之后弹出的元素需要加上的值。
复杂度为 O(Tn3)

代码
#include<bits/stdc++.h>
using namespace std;

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmin(a,b) a=min(a,b)
#define chkmax(a,b) a=max(a,b)
#define LL long long

void Rd(int &res){
    char c;res=0;
    while((c=getchar())<48);
    do res=(res<<3)+(res<<1)+(c^48);
    while((c=getchar())>47);
}
#define M 104
int T,Case,n,A[M],S[M];
int DP[M][M];
#define INF 0x3f3f3f3f
int DFS(int l,int r){
    if(l>=r)return 0;
    int &Now=DP[l][r];
    if(Now!=INF)return Now;
    REP(i,l,r+1)chkmin(Now,(i-l)*A[l]+DFS(l+1,i)+DFS(i+1,r)+(i-l+1)*(S[r]-S[i]));
    return Now;
}
int main(){
    Rd(T);
    while(T--){
        Rd(n);REP(i,1,n+1)Rd(A[i]),S[i]=S[i-1]+A[i];
        memset(DP,63,sizeof(DP));
        printf("Case #%d: %d\n",++Case,DFS(1,n));
    }
    return 0;
}

T2 SS

期望值显然是每个降落点的概率乘上其到最近大本营的距离。
那么我们就要考虑每个大本营管理的点的位置。
Costi,j 表示区间 [i,j] 放一个大本营最小所需的期望。
然后这是一个绝对值函数,取中值即可得到最小。
那么前缀和优化,用一个指针取其位置即可。
剩下的DP应该就比较简单了。

代码
#include<bits/stdc++.h>
using namespace std;

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define DB double

#define M 1004
int n,m;
map<int,DB>V;
int Pos[M];
DB Val[M],Sum[M];
DB F[54][M],Cost[M][M];
int main(){
    while(scanf("%d %d",&n,&m)!=EOF){
        if(!n)break;
        V.clear();
        REP(i,0,n){
            int L,x;
            DB p;
            scanf("%d",&L);
            while(L--)scanf("%d %lf",&x,&p),V[x]+=p;
        }
        n=0;
        for(map<int,DB>::iterator it=V.begin();it!=V.end();it++)
            Pos[++n]=it->first,Val[n]=it->second;

        REP(i,1,n+1)Sum[i]=Sum[i-1]+Val[i]*Pos[i],Val[i]+=Val[i-1];

        REP(i,1,n+1){
            int p=i;
            REP(j,i,n+1){
                while(p<j && Val[p]<(Val[j]+Val[i-1])/2)p++;
                Cost[i][j]=Pos[p]*(Val[p]-Val[i-1])-(Sum[p]-Sum[i-1])
                        +(Sum[j]-Sum[p])-Pos[p]*(Val[j]-Val[p]);
            }
        }

        REP(i,0,54) REP(j,1,M)F[i][j]=1e15;
        REP(i,0,m)F[i][0]=0;
        REP(i,0,m) REP(j,1,n+1) REP(k,j,n+1)
            chkmin(F[i+1][k],F[i][j-1]+Cost[j][k]);

        printf("%.2lf\n",F[m][n]);
    }
    return 0;
}

T3 E

给出 n 个元素和n1个运算符,只有‘+’,‘-’,‘*’求所有合并方式得到的结果之和,模 109+7
显然定义状态 Fi,j 为区间 [i,j] 所得到的答案。
然后考虑如何合并两个区间的结果来得到当前区间的某个划分方案。
如果是运算符是‘+’或‘-’,可知:
区间的答案个数是其长度减一的阶乘。
然后 a1+a2 b1+b2+b3 相加后得到的是
3(a1+a2)+2(b1+b2+b3)
即阶乘互乘后再相加减。
而运算符为‘*’时,直接相乘即可。
另外,由于左右两侧的操作是可以相互交替进行的,
还需要乘上一个组合数。

代码
#include<bits/stdc++.h>
using namespace std;

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define DB double

#define M 104
#define Mod 1000000007
#define Add(a,b) (a+=b)%=Mod
#define Mul(a,b) (1ll*(a)*(b)%Mod)

int n,Fac[M],F[M][M],G[M][M];
char Op[M];
int Calc(int x,int y,char p){
    if(p=='+') return (x+y)%Mod;
    if(p=='-') return (x+Mod-y)%Mod;
    return Mul(x,y);
}
int main(){
    Fac[0]=1;
    REP(i,1,M)Fac[i]=Mul(Fac[i-1],i);
    REP(i,0,M)G[i][0]=G[0][i]=1;
    REP(i,1,M)REP(j,1,M)G[i][j]=(G[i][j-1]+G[i-1][j])%Mod;
    while(scanf("%d",&n)!=EOF){
        memset(F,0,sizeof(F));
        REP(i,1,n+1)scanf("%d",&F[i][i]);
        scanf("%s",Op+1);
        REP(l,2,n+1)REP(i,1,n-l+2){
            int j=i+l-1,q=0,p=l-2;
            REP(k,i,j){
                Add(F[i][j],Mul(G[q][p],Calc(Mul(Op[k]=='*'?1:Fac[p],F[i][k]),Mul(Op[k]=='*'?1:Fac[q],F[k+1][j]),Op[k])));
                q++,p--;
            }
        }
        printf("%d\n",F[1][n]);
    }
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值