C. Remove the Bracket

题目:

题目链接

这是一道数学+dp题

前期数学分析,后期dp求答案

代码如下:思路在注释

思路:因为数组中的数都是正整数,所以(xi-s)*(yi-s)0就等价于min(xi,yi)s或max(xi,yi)s。

要把一个数分成两个数,最优的方案就是一个是合法的最大数,另一个是最小的,因为只要这个数原来两边的数和不一样,如果分成的两个数一个数+1,一个数-1,那么乘积的和就会+或-和的差,所以只有xi,yi都是极端状态才可能成为最优方案。所以当这个数小于等于s时,xi,yi中的最大值小于等于s,所以就是这个数本身,最小值就是0,当这个数大于s时,如果这个数小于等于两倍的s,那么最大值就是s,最小值就是ai-s,如果这个数大于两倍的s,那么最大值是ai-s,最小值是s,所以综上,这个数大于s时最大值就是max(s,ai-s),最小值等于ai-最大值。那么我们dp枚举即可,dp[i][0]表示当前数分成最大数在前,最小数在后,dp[i][1]表示当前数分成最大数在后,最小数在前,每个dp[i]都从dp[i-1]的两种状态中的最优值转移即可

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
#define buug() std::cerr<<"\t---------debug---------\n"
#define bug(n) std::cerr<<#n<<": "<<n<<endl
using namespace std;
using ULL=unsigned long long;
//using LL=long long;
using PII=pair<int,int>;




void debug() {std::cerr << "\n";}
template<class T, class... Args>
void debug(T v, Args... args) {
	std::cerr <<v<< " ";
	debug(args...);
}



const int N=2e5+10,MX=0x3f3f3f3f;

int n,t,m,k;
int ar[N];


signed main(){
	ios::sync_with_stdio(false);cin.tie(0);
	//fstream cin;	cin.open("in.txt",ios::in);
	
	cin>>t;
	while(t--){
		
		//参考文章https://blog.csdn.net/ashbringer233/article/details/128804893
		
		cin>>n>>k;
		vector<int> ma(n+1),mi(n+1);
		for(int i=1;i<=n;++i){
			int x;
			cin>>x;
			if(i==1||i==n){//依照题意,两边不拆分
				ma[i]=mi[i]=x;
				continue;
			}
			if(x<=k){
				ma[i]=x;mi[i]=0;//将x安装区域划分为不同的最大最小值
			}
			else {
				int x1=k,x2=x-k;
				ma[i]=max(x1,x2);
				mi[i]=x-ma[i];
			}
		}
		
		vector<vector<int>> dp(n+1,vector<int>(2));
		for(int i=2;i<=n;++i){
			/*
			  dp[i][0]:吧最大值放在前面的到当前i时所求式子的最小值
			  dp[i][1]:吧最小值放在前面的到当前i时所求式子的最小值;
			 */
			dp[i][0]=min(dp[i-1][0]+mi[i-1]*ma[i],dp[i-1][1]+ma[i-1]*ma[i]);
			dp[i][1]=min(dp[i-1][0]+mi[i-1]*mi[i],dp[i-1][1]+ma[i-1]*mi[i]);
		}
		int res=min(dp[n][0],dp[n][1]);
		cout<<res<<endl;
		
	}
	
	
	
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36738806/article/details/131050878