信息学奥赛一本通 5.1 区间类动态规划

石子合并[loj 10147]

/*
dp[i][j]=max or min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
i<=k<j
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
inline int read()
{
    int f=1,ans=0;char c;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();}
    return f*ans;
}
int a[401],n,dp_minn[401][401],dp_maxn[401][401],sum[401];
int main()
{
    memset(dp_minn,127,sizeof(dp_minn));
    n=read();
    for(int i=1;i<=n;i++)
    {
        dp_minn[i][i]=dp_minn[i+n][i+n]=0;
        a[i]=read(),a[i+n]=a[i];
    }
    for(int i=1;i<=2*n;i++) sum[i]=sum[i-1]+a[i];
    for(int t=2;t<=n;t++)
        for(int i=1;i<=2*n&&i+t-1<=2*n;i++)
        {
            int j=i+t-1;

            for(int k=i;k<j;k++)
            {
                dp_maxn[i][j]=max(dp_maxn[i][j],dp_maxn[i][k]+dp_maxn[k+1][j]+sum[j]-sum[i-1]);
                dp_minn[i][j]=min(dp_minn[i][j],dp_minn[i][k]+dp_minn[k+1][j]+sum[j]-sum[i-1]);
            }//cout<<i<<" "<<j<<" "<<dp_minn[i][j]<<endl;
        }
    int maxn=0,minn=2<<30-1;
    for(int i=1;i<=n;i++) maxn=max(maxn,dp_maxn[i][i+n-1]),minn=min(minn,dp_minn[i][i+n-1]);
    cout<<minn<<endl<<maxn;
}

能量项链[loj 10148]

/*
为了好想 记录每一的head和tail
so dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+head[i]*tail[k]*tail[j])
i<=k<j
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
inline long long read()
{
    long long f=1,ans=0;char c;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();}
    return f*ans;
}
long long dp[201][201],a[201],h[201],t[201];
int main()
{
    long long n=read();
    for(long long i=1;i<=n;i++) a[i]=read(),a[i+n]=a[i];
    for(long long i=1;i<2*n;i++) h[i]=a[i],t[i]=a[i+1];
//    for(int i=1;i<2*n;i++) cout<<h[i]<<" "<<t[i]<<endl;
    for(long long tt=1;tt<n;tt++)
        for(long long i=1;i<=2*n&&i+tt<=2*n;i++)
        {
            long long j=i+tt;
//            cout<<i<<" "<<j<<endl;
            for(long long k=i;k<j;k++) dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+h[i]*t[k]*t[j]);
        }
    long long maxn=0;
    for(long long i=1;i<=n;i++) maxn=max(maxn,dp[i][i+n-1]);
    cout<<maxn;
}
/*
7
23 17 212 113 71 301 33
31182687
*/

凸多边形的划分[loj 10149]

/*
高精度或__int128
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+a[i]*a[k]*a[j])
dp[i][j][0]存位数
其他四个数一个int
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline long long read()
{
    long long f=1,ans=0;char c;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();}
    return ans*f;
}
long long n,a[51];
long long dp[110][110][110];
long long s1[110],s2[110],s3[110];
void print()
{
    cout<<dp[1][n][dp[1][n][0]];
    for(long long i=dp[1][n][0]-1;i>=1;i--)
    {
        cout<<dp[1][n][i]/1000;
        cout<<dp[1][n][i]/100%10;
        cout<<dp[1][n][i]/10%10;
        cout<<dp[1][n][i]%10;
    }
}
void mark(long long c[])
{
    for(long long i=1;i<=c[0];i++)
    {
        c[i+1]+=c[i]/10000;
        c[i]%=10000;
    }
    while(c[c[0]+1]!=0)
    {
        c[0]++;
        c[c[0]+1]=c[c[0]]/10000;
        c[c[0]]%=10000;
    }
}
void mul(long long a1,long long a2,long long a3,long long c[])
{
    c[0]=c[1]=1;
    for(long long i=1;i<=c[0];i++) c[i]*=a1;
    mark(c);
    for(long long i=1;i<=c[0];i++) c[i]*=a2;
    mark(c);
    for(long long i=1;i<=c[0];i++) c[i]*=a3;
    mark(c);
}
void add(long long a[],long long b[],long long c[])
{
    if(a[0]>b[0]) c[0]=a[0];
    else c[0]=b[0];
    for(long long i=1;i<=c[0];i++) c[i]=a[i]+b[i];
    mark(c);
}
long long compare(long long a[],long long b[])
{
    if(a[0]>b[0]) return 1;
    if(a[0]<b[0]) return 0;
    for(long long i=a[0];i>=1;i--)
    {
        if(a[i]<b[i]) return 0;
        else if(a[i]>b[i]) return 1;
    }
    return 0;
}
int main()
{
    n=read();
    for(long long i=1;i<=n;i++) a[i]=read();
    for(long long t=2;t<=n-1;t++)
    {
        for(long long i=1;i+t<=n;i++)
        {
            long long j=i+t;
            dp[i][j][0]=60;
            for(long long k=i+1;k<=j-1;k++)
            {
                memset(s1,0,sizeof(s1));
                memset(s2,0,sizeof(s2));
                memset(s3,0,sizeof(s3));
                mul(a[i],a[k],a[j],s1);
                add(dp[i][k],dp[k][j],s2);
                add(s1,s2,s3);
                if(compare(dp[i][j],s3))
                    memcpy(dp[i][j],s3,sizeof(s3));
            }
        }
    }
    print();
}

括号配对[loj 10150]

/*
dp[i][j]表示在[i,j]中至少需要添加的数量
1.  (i==[  j==] )  or(i==(   j==))  dp[i][j]=min(dp[i][j],dp[i+1][j-1]
2.  i==( or  i==[ dp[i][j]=min(dp[i][j],dp[i+1][j])
3.  j==) or j==]  dp[i][j]=min(dp[i][j],dp[i][j-1])
4. dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]) (i<=k<j) 

初始化时dp[i][j]=inf  (i<=j) dp[i][i]=1;(1<=i<=n)
想一想为什么
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
inline int read()
{
    int f=1,ans=0;char c;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();}
    return f*ans;
}
char str[102];
int a[102];
int dp[110][110],inf=2<<30-1;
int main()
{
//    memset(dp,127,sizeof(dp));

    scanf("%s",str+1);
    int len=strlen(str+1);
    for(int i=1;i<=len;i++)
        for(int j=i;j<=len;j++) dp[i][j]=inf;
    for(int i=1;i<=len;i++)
    {
        dp[i][i]=1;
        if(str[i]==‘(‘) a[i]=1;
        if(str[i]==‘)‘) a[i]=2;
        if(str[i]==‘[‘) a[i]=3;
        if(str[i]==‘]‘) a[i]=4;
    }
    for(int t=1;t<len;t++)
    {
        for(int i=1;i+t<=len;i++)
        {
            int j=i+t;
            if((a[i]==1&&a[j]==2)||(a[i]==3&&a[j]==4)) dp[i][j]=min(dp[i][j],dp[i+1][j-1]);
            if(a[i]==1||a[i]==3) dp[i][j]=min(dp[i][j],dp[i+1][j]+1);
            if(a[j]==2||a[j]==4) dp[i][j]=min(dp[i][j],dp[i][j-1]+1);
            for(int k=i;k<j;k++) dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
//            cout<<i<<" "<<j<<" "<<dp[i][j]<<endl;
        }
    }
    cout<<dp[1][len];
}

分离与合体[loj 10151]

/*
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+(a[i]+a[j])*a[k])
i<=k<j

用last记录
bfs保证了一分为二,二分为四的输出
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
inline int read()
{
    int f=1,ans=0;char c;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();}
    return f*ans;
}
int n,a[301],dp[301][301],last[301][301];
void bfs()
{
    queue< pair <int,int > > que;
    que.push(make_pair(1,n));
    while(!que.empty())
    {
        pair<int,int> s=que.front();
        que.pop();
        if(s.first==s.second) continue;
        cout<<last[s.first][s.second]<<" ";
        que.push(make_pair(s.first,last[s.first][s.second]));
        que.push(make_pair(last[s.first][s.second]+1,s.second));
    }
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int t=1;t<n;t++)
        for(int i=1;i+t<=n;i++)
        {
            int j=i+t;
            for(int k=j-1;k>=i;k--)
            {
                int t=dp[i][k]+dp[k+1][j]+(a[i]+a[j])*a[k];
                if(t>=dp[i][j])
                {
                    dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+(a[i]+a[j])*a[k]);
                    last[i][j]=k;
                }
            }
        }
    cout<<dp[1][n]<<endl;
    bfs();
    return 0;
}

矩阵取数游戏[loj 10152]

/*
一行一行处理
dp[i][j]表示变到 i,j时的最大分数
dp[i][j]=max(dp[i][j],dp[i-1][j]*a[i-1]^pow(2,n+i-j-1),dp[i][j+1]*a[j+1]^pow(2,n+i-j-1))
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
inline long long read()
{
    long long f=1,ans=0;char c;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();}
    return f*ans;
}

__int128 dp[101][101],ans,sry[101];
long long n,m,a[101];
void print(__int128 x)
{
    if(x==0) return;
    else if(x!=0) print(x/10);
    putchar(x%10+‘0‘);
}
int main()
{
    sry[0]=1;
    for(int i=1;i<=100;i++) sry[i]=sry[i-1]*2;
    n=read(),m=read();
    for(long long i=1;i<=n;i++)
    {
        for(long long j=1;j<=m;j++) a[j]=read();
        memset(dp,0,sizeof(dp));
        for(long long i=1;i<=m;i++)
            for(long long j=m;j>=i;j--)
                dp[i][j]=max(dp[i-1][j]+a[i-1]*sry[m-j+i-1],dp[i][j+1]+a[j+1]*sry[m-j+i-1]);
        __int128 maxn=-1;
        for(long long i=1;i<=m;i++) maxn=max(maxn,dp[i][i]+a[i]*sry[m]);
        ans+=maxn;
    }
    if(ans==0)
    {
        cout<<0;return 0;}
    print(ans);
}

原文地址:https://www.cnblogs.com/si-rui-yang/p/9502515.html

时间: 2024-08-01 07:04:08

信息学奥赛一本通 5.1 区间类动态规划的相关文章

信息学奥赛一本通 5.4 状态压缩动态规划

#loj 10170. 「一本通 5.4 例 1」骑士 看数据范围n<=10,所以不是搜索就是状压dp,又因为搜索会超时所以用dp dp[i][k][j]表示现已经放到第i行,前面共有k个,这一行状态为j so,dp[i][k][j]=dp[i-1][k-num[j]][t] #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cma

信息学奥赛一本通 5.2 树形动态规划

题解在代码中 二叉苹果树[loj 10153] /* 若要留q条边便是要留q+1个点 所以记忆化搜索 dp[pos][ans]=max(dp[pos][ans],dp[l[pos]][k]+dp[r[pos]][ans-k-1]+a[pos]) 0<=k<=ans-1 */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<c

【信息学奥赛一本通 提高组】第二章 二分与三分

一.二分 二分法,在一个单调有序的集合或函数中查找一个解,每次分为左右两部分,判断解在那个部分并调整上下界,直到找到目标元素,每次二分都将舍弃一般的查找空间,因此效率很高. 二分常见模型 1.二分答案 最小值最大(或是最大值最小)问题,这类双最值问题常常选用二分法求解,也就是确定答案后,配合贪心,DP等其他算法检验这个答案是否合理,将最优化问题转化为判定性问题.例如,将长度为n的序列ai分为最多m个连续段,求所有分法中每段和的最大值的最小是多少? 2.二分查找 用具有单调性的布尔表达式求解分界点

[信息学奥赛一本通oj1741]电子速度 题解

对于$100 \%$的数据,$1≤n,m≤1e6 \ \ \ 0<=x_i,y_i<20170927 \ \ \ 1≤l_i,r_i≤n $ $Solution:$ 一开始没看懂题.后来大致理解了一下,所谓的v是一个二维向量,有x和y两个参数.那个$\times$是叉乘,即$(x_i y_j-x_j y_i)$. 所以题意就是给你一个x序列和y序列,对于每次询问的区间$[l,r]$,求$\sum \limits _{l\leq i<j \leq r}(x_iy_j-x_jy_i)^2$.

【信息学奥赛一本通】Part1.2 基础算法-二分与三分

问题 A: [二分和三分]愤怒的牛 题目描述 农夫约翰建造了一座有n间牛舍的小屋,牛舍排在一条直线上,第i间牛舍在xi的位置,但是约翰的m头牛对小屋很不满意,因此经常互相攻击.约翰为了防止牛之间互相伤害,因此决定把每头牛都放在离其它牛尽可能远的牛舍.也就是要最大化最近的两头牛之间的距离. 牛们并不喜欢这种布局,而且几头牛放在一个隔间里,它们就要发生争斗.为了不让牛互相伤害.John决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是多少呢? 输入 第一行用空格分

【信息学奥赛一本通】第三部分_栈 ex1_4cale (中缀转后缀7符号)

其实这个中缀转后缀是费了很大功夫的,明白算法后第一次实现花了近三小时ORZ #include <stdio.h> #include <string.h> #include <ctype.h> char Mstr[511],Msta[511] = {'@'},Bstr[511]; int sta[511]; const short list[4][4] = {{0,-1,1,1},{1,0,1,1},{1,1,1,-2},{-1,-1,2,1}}; int level (

【信息学奥赛一本通】第三部分_队列 ex2_3produce 产生数

给出一个整数n(n<=2000)(代码可适用n<=10^31)和k个变换规则(k<=15). 规则:1.1个数字可以变换成另1个数字: 2.规则中右边的数字不能为零. BFS 1 #include <stdio.h> 2 #include <string.h> 3 #define maxn 1000 4 5 char num[33]; 6 int len,q[maxn],Visited[11]; 7 long long ans = 1; 8 9 int main

信息学奥赛一本通 提高篇 序列第k个数 及 快速幂

我是传送门 这个题首先是先判断是等差还是等比数列 等差的话非常简单: 前后两个数是等差的,举个栗子: 3 6 9 12 这几个数,(我感觉 1 2 3 4并说明不了什么) 每次都加3嘛,很容易看出,第一个数是3 * 1,第二个是3 * 2....以此类推 第k个数 = (第2个数 - 第1个数) * k ; (z - y) * k % 200907 % 200907 的原因是题目要求 但是这样并不能过 hack一下 4 7 10 13 用原先的公式:(7 - 4) * 4 % 200907 =

求后序遍历(信息学奥赛一本通 1339)

假设有棵树,长下面这个样子,它的前序遍历,中序遍历,后续遍历都很容易知道. PreOrder: GDAFEMHZ InOrder: ADEFGHMZ PostOrder: AEFDHZMG 现在,假设仅仅知道前序和中序遍历,如何求后序遍历呢?比如,已知一棵树的前序遍历是"GDAFEMHZ",而中序遍历是"ADEFGHMZ"应该如何求后续遍历? 第一步,root最简单,前序遍历的第一节点G就是root. 第二步,继续观察前序遍历GDAFEMHZ,除了知道G是root,