【区间DP】

【区间DP】

板子题

拓展题

可划分数组

https://ac.nowcoder.com/acm/contest/105825/E
image

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
const int INF=0x3f3f3f3f;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=2e3+10;
int n;
int a[N];
int pre[N],nxt[N];
//pre:在pre[i]前的第一个不互质的数
//nxt:在nxt[i]后的第一个不互质的数
int dp[N];
int gcd(int a,int b){
	return b?gcd(b,a%b):a;
}
signed main(){
      ios::sync_with_stdio(0);
      cin.tie(0);
      cout.tie(0);
      cin>>n;
      for(int i=1;i<=n;i++) cin>>a[i];
      //算pre和nxt
      for(int i=1;i<=n;i++){
            for(int j=i-1;j>=1;j--){
                  if(gcd(a[i],a[j])!=1){
                        pre[i]=j;
                        break;
                  }
            }
      }
      for(int i=1;i<=n;i++){
            nxt[i]=n+1;//记得初始化n+1(最右边!)
            for(int j=i+1;j<=n;j++){
                  if(gcd(a[i],a[j])!=1){
                        nxt[i]=j;
                        break;
                  }
            }
      }
      //对dp数组初始化:初始化长度为1的和为2的
      for(int r=2;r<=n;r++){//r从2开始
            dp[r]=1;
            for(int i=1;i<=r;i++){
                  //不满足条件=0:pre和nxt
                  if(!(pre[i]>=1 || nxt[i]<=r)) dp[r]=0;
            }
      }
      //[l,i][i+1,r]
      //i从2枚举到n-2->r从4枚举到n
      for(int r=4;r<=n;r++){
            //如果nxt[r]取得到:没必要先取pre,直接INF
            int minpre=nxt[r]>r?pre[r]:INF;
            for(int i=r-1;i>=3;i--){//枚举i+1 双指针式扩展
                  if(nxt[i]>r) minpre=min(minpre,pre[i]);//扩展条件
                  if(minpre>=i && dp[i-1]!=0){//判断两个条件均满足
                        dp[r]=max(dp[r],dp[i-1]+1);//注意这里-1是因为枚举的i+1
                  }
            }
      }
      if(dp[n]==0){
            cout<<"-1";
            return 0;
      }
      cout<<dp[n];
      return 0;
}

Iron Bars Cutting

https://ac.nowcoder.com/acm/contest/108298/I

题目大意

image
image

思路

image
image
image
->sort不平衡度小->大/区间小->大

dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]+贡献)
记得初始化dp[i][i]=0

代码

int n;
void solve(){
    cin>>n;
	vector<i64> a(n+1,0);
	vector<i64> pre(n+1,0);
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) pre[i]=pre[i-1]+a[i];
	int len=0;
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			for(int k=i;k<j;k++){
				len++;
			}
		}
	}
	vector<array<i64,4>> pos(len);
	int cnt=0;
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			for(int k=i;k<j;k++){
				i64 l1=pre[k]-pre[i-1],l2=pre[j]-pre[k];
				i64 val=min64(l1,l2)*(__lg(l1+l2-1LL)+1LL);
				pos[cnt++]={i,j,k,val};
			}
		}
	}
    sort(pos.begin(),pos.end(),
	[&](array<i64,4> x,array<i64,4> y)->bool{
		auto [l1,r1,k1,v1]=x;
		auto [l2,r2,k2,v2]=y;
		i64 c1=llabs((pre[k1]-pre[l1-1])-(pre[r1]-pre[k1]));
		i64 c2=llabs((pre[k2]-pre[l2-1])-(pre[r2]-pre[k2]));
		if(c1==c2){
			return (r1-l1+1)<(r2-l2+1);
		}
		else{
			return c1<c2;
		}
	});
	vector<i64> res(n+1,1e18);
	vector<vector<i64>> dp(n+1,vector<i64>(n+1,1e18));
	//初始化
	for(int i=1;i<=n;i++){
		dp[i][i]=0;
	}
	for(auto [l,r,k,v]:pos){
		i64 cv=dp[l][k]+dp[k+1][r]+v;
		dp[l][r]=min64(dp[l][r],cv);
		if(l==1 && r==n){
			res[k]=cv; //更新的是算得到的
		}
	}
	for(int i=1;i<n;i++){
		if(res[i]>=1e18) cout<<-1<<" ";
		else cout<<res[i]<<" ";
	}
	cout<<endl;
}
posted @ 2025-03-29 12:33  White_ink  阅读(8)  评论(0)    收藏  举报