[Acwing] 算法提高课汇总一 动态规划-区间DP

区间DP

1.环形石子合并

题意 :
环状石子合并相邻的两堆,询问最大最小开销

思路 :
状态表示 :
f [ l ] [ r ] f[l][r] f[l][r]表示从 l l l开始到 r r r的花费

状态计算 :
f [ l ] [ r ] = m i n / m a x ( f [ l ] [ r ] , f [ l ] [ k ] + f [ k ] [ r ] + s [ r ] − s [ l − 1 ] ) f[l][r] = min/max(f[l][r],f[l][k]+f[k][r]+s[r]-s[l-1]) f[l][r]=min/max(f[l][r],f[l][k]+f[k][r]+s[r]s[l1])

因为是环形问题,我们考虑开两倍的空间用于存放

那么对于 i i i就对应 i + n − 1 i+n-1 i+n1

code :

int n;
int w[N],s[N];
int f[N][N],g[N][N];
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>w[i];
        w[i+n] = w[i];///两倍数组
    }

    for(int i=1;i<=n*2;i++)
    s[i] = s[i-1]+w[i]; ///预处理前缀和

    memset(f,0x3f,sizeof f);
    memset(g,-0x3f,sizeof g);

    for(int len = 1;len<=n;len++)
    {
        for(int l = 1;l+len-1<=n*2;l++)
        {
            int r = l+len-1;
            if(l == r) g[l][r] = f[l][r] = 0;///如果区间长度是1
            else
            {
                for(int k=1;k<r;k++)///状态转移i计算
                {
                     g[l][r] = max(g[l][r],g[l][k]+g[k+1][r]+s[r]-s[l-1]);

                     f[l][r] = min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
                }
            }
        }
    }

    int minv = INF ,maxv = -INF;
    for(int i=1;i<=n;i++)
    {
        minv =min(minv,f[i][i+n-1]);
        maxv =max(maxv,g[i][i+n-1]);
    }
    cout<<minv<<'\n'<<maxv<<'\n';
}
2.能量项链

题意 :
给定一个项链对于上面的珠子有两个值 r , t r,t r,t表示首和尾,你可以让两个相邻的珠子进行合并,合并产生的开销是 r 1 ∗ t ∗ r 2 r_1*t*r_2 r1tr2,询问最后变为一个珠子的最小开销

思路 :
处理方法等同于环形石子合并,但是又有很多细节上的差异

因为最终合并的原因导致最后数组的长度是 n + 1 n+1 n+1, 最后答案输出需要是 f [ i ] [ i + 1 ] f[i][i+1] f[i][i+1]

code :

int n;
int w[N],s[N];
int f[N][N],g[N][N];
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>w[i];
        w[i+n] = w[i];///两倍数组
    }


    memset(f,-0x3f,sizeof f);
   
    for(int len = 2;len <= n+1; len ++ ){//最后长度可以达到n=1
    	for(int l=1;l+len-1<=n*2; l ++ ){
    		int r = l + len-1;
    		if(len == 2)f[l][r] = 0 ;
    		else 
    		for(int k = l+1;k<r;k++){
    			f[l][r] = max(f[l][r],f[l][k]+f[k][r]+w[k]*w[r]*w[l]);
    		}
    	}
    }
    
    int res = 0 ;
    for(int i=1;i<=n;i++)
    res = max(res,f[i][i+n]);
    cout<<res<<endl;
    
}
4.凸边形的划分

题意 :
给定一个 N N N个顶点的凸多边形,每个顶点都有一个权值,让你将这个多边形划分为 N − 2 N-2 N2个互不相交的三角形,对于每个三角形其顶点相乘可用获得一个权值,询问所有三角形权值乘积之和最少是多少

思路 :
我们假定选中一个三角形

在这里插入图片描述
又因为题目中给定的,互不相交,因此左右两边独立,所以这时候就很像区间dp

状态表示 :
f [ L , R ] f[L,R] f[L,R]表示 所有将 ( L , L + 1 ) , ( L + 1 , L + 2 ) . . . . ( R , L ) (L,L+1),(L+1,L+2)....(R,L) (L,L+1),(L+1,L+2)....(R,L)这个多边形划分为三角形的最小方案

状态转移 :
f [ L , R ] = f [ L , k ] + f [ k , R ] + w [ L ] ∗ w [ k ] ∗ w [ R ] f[L,R] =f[L,k]+f[k,R]+w[L]*w[k]*w[R] f[L,R]=f[L,k]+f[k,R]+w[L]w[k]w[R]

同时有因为这个题需要使用高精度, y y y总在这里用了一个偷懒的办法

就是多开一维数组记录每个位置的数

code :

const int N = 55,INF = 0x3f3f3f3f , M = 35;//保守点取35位
const double eps = 1e-5;


struct node{
    int to,val;
};

vector<int> f[N][N];
//f[l][r] 表示(l,l+1)...(l,r)划分成三角形所有方案的最小值

int w[N];//记录点权
int n;

vector<int> add(vector<int>& a,vector<int>& b){
    int t = 0;
    vector<int> c;
    for(int i = 0;i < a.size() || i < b.size();++i){ 
        if(i < a.size()) t += a[i];
        if(i < b.size())  t += b[i];
        c.push_back(t % 10);
        t /= 10;
    }
    if(t) c.push_back(t);
    return c;
}

vector<int> mul(vector<int>& a,LL b){
    vector<int> c;
    LL t = 0;
    for(int i = 0;i < (int)a.size() || t;++i){
        if(i < a.size()) t += a[i] * b;
        c.push_back(t % 10);
        t /= 10;
    }
    return c;
}


void reslove(vector<int>& t,int num){  //将num分解后加入到t中
    while(num){
        t.push_back(num % 10);
        num /= 10;
    }
    if(num) t.push_back(num);
}

bool cmp(vector<int>& a,vector<int>& b)
{
    if (a.size() != b.size()) return a.size() > b.size();
    int i = (int)a.size() - 1;
    while (a[i] == b[i] && i > 0) --i;
    return a[i] > b[i];
}

void solve(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>w[i];
	
	//三条边才可以构建一个三角形
	for(int len = 3;len<=n;len ++ ){
		for(int l=1;l+len-1<=n;l ++ ){
			int r = l+len-1;
			//枚举区间的底
			
			for(int  i = 0;i<M; i ++ ){
				f[l][r].pb(1);
			}//初始化为正无穷
			
			//枚举中间的分割点
			for(int k = l+1 ;k<=r-1 ;k ++ ){
				vector<int> temp;
				reslove(temp,w[1]);
				mul(temp,w[k]);
				mul(temp,w[r]);
				add(temp,f[l][k]);
				add(temp,f[k][r]);
				if(cmp(temp,f[l][r]))
				f[l][r] = temp;
				
			}
		}
	}
	
	for(int i=n-1;i>=0;i--)
	cout<<f[1][n][i];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值