区间dp 石子合并+cf

282. 石子合并 - AcWing题库

题意是给你一堆石子,每次合并都只合并相邻的两堆,代价是两堆的石子质量,问你合并到最后(合并到一堆)最小代价为多少

因为最后都要划归到一个问题上,就是合并到最后是左右两边连续的区间,这可以想到区间dp

令f【i,j】为把从i到j的这一段合并的最小代价

整个石子,从1到n,有很多个区间,所以这个时候需要枚举区间然后对每个ij分别进行计算

对于从i到j这单独的一个区间,刚刚上面也说了。对于一个区间它最后的分法必然是左边的连续的一段和右边连续的一段。

所以对于这个[i,j]来说,设这个区间的分界点是k,然后一个整区间i,j可以变成[i,k]和[k+1,j],

———k——————

如图上面的两段,那f[i,j]=f[i,k]+f[k+1,j]+s[i,j]

就是先有两堆,两堆的代价分别是f[i,k]和f[k+1,j]然后两堆要合并到一堆,那没有区别,直接全部都加起来好了

#include<iostream>
using namespace std;
const int N=500;
int f[N][N],a[N];
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        a[i]+=a[i-1];
    }
    for(int len=2;len<=n;len++)//如果len是1,那整个ij区间就变成了长度为1的区间,没必要合并
    {
        for(int i=1;i+len-1<=n;i++)
        {
            int j=i+len-1;
            f[i][j]=1e8;//求的是每一个ij的最小值,k作为划分点枚举
            for(int k=i;k<j;k++)
                f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+a[j]-a[i-1]);
        }
    }
    cout<<f[1][n];
    return 0;
}

D2. Zero-One (Hard Version)

Problem - D2 - Codeforces

题意是给你两个01串ab,可以选择两个索引i和j,把0变成1,1变成0。改变是有代价的,如果索引相邻的话,代价为x,不相邻的代价为y,问你让ab相等的最小代价是多少

这题要注意范围...我还在边界上面考虑,然后感觉边界不好判断啊,感觉挺复杂的,最后看了一下n的范围最小是5.有的东西就不用考虑了

奇数直接-1

下面开始分类讨论了:(如果d1可以只看第一类)

当x>=y的时候,那就是优先选择y,如果不同的个数大于2的话,可以两辆进行抵消,并且不会有相邻,代价就是:cnt/2*y;

当x<y的时候,就不只只是贪心了,还要用到dp:区间dp

区间dp的本质是枚举每一个区间,然后在单独的区间里面进行划分,进行状态的转移(区间dp的状态转移其实不复杂,正常写其实就好了)

刚刚上面的那个题,他是合并石子,不管怎么分,在某一段区间里面,他最终的结果都是把他们划分成左右两边连续的两段。所以这个时候,可以新引入一个变量k,然后变成左右两段,最后取每一个区间取最小值,最后变成整个完整的区间

那这个题的区间dp划分的依据又在什么地方呢?

这题是x<y,显然x的优势大于y,优先考虑x

那不如就以这个来划分依据

因为是相邻的又必然是偶数,所以可以都选择用x,那么要使x发挥作用,必然是区间越大越好,那就是变成了三种情况(不连第一类的那种)

下面就看代码好了

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define IOS ios::sync_with_stdio(false), cin.tie(0);
#include<iostream>
#include<map>
#include<set> 
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<algorithm>
#include<cmath>
#include<queue>
#include<deque>
using namespace std;
#define int long long
typedef long long LL;
typedef pair<int,int> PAII;
const int N=2e6+10,M=5005,INF=1e9,mod=1e9+7;
vector<int> ve;
int n,x,y;
int f[M][M];
int cal(int a,int b)
{
	int dis=ve[b]-ve[a];
	if(dis==1) return min(x,y*2);
	else return min(x*dis,y);
}
signed main(){
    IOS;
    int T;
    //T=1;
    cin>>T;
    while(T--)
    {
	  	ve.clear();
		cin>>n>>x>>y;
		string a,b;
		cin>>a>>b;
		for(int i=0;i<n;i++)
		{
			if(a[i]!=b[i])
				ve.push_back(i);
		}
		int len = ve.size();
		if(len%2 == 1) {
			cout<<"-1\n";
			continue;
		}
		
		for (int k = 2; k <= len; k += 2 ) {
			for (int i = 0; i+k-1 < len; i ++ ) {
				int j=i+k-1;
				if(k==2) f[i][j]=cal(i,j);
				else f[i][j]=min({f[i+1][j-1]+cal(i,j), f[i+2][j]+cal(i,i+1), f[i][j-2]+cal(j-1,j), y*len/2});
			}
		}
		cout<<f[0][len-1]<<"\n";
    }
    return 0;
}
/*
f[i][j]=f[i][j-2]+cal(j-1,j);
f[i][j]=f[i+2][j]+cal(i,i+1);
f[i][j]=f[i+1][j-1]+cal(i,j);
nn/2*y
*/

********************************

(4条消息) D. Letter Picking edu135 div2_三木森sam的博客-CSDN博客

区间dp + 博弈论

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值