题目:
这是一道数学+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.youkuaiyun.com/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;
}