题意:给定一个长度为n的数组a和一个常数s,找到2~n-1的数的合适的分解方法使得a[i]=xi+yi, 要满足(xi−s)⋅(yi−s)≥0&&xi>=0,yi>=0
使得 F=a1⋅x2+y2⋅x3+y3⋅x4+…+yn−2⋅x(n−1)+y(n−1)⋅an 的F的值最小
思路: 根据题目给的限制,a[i]的分解存在两种情况,
第一种:当a[i]<=s时分解为0和a[i] ,
第二种:当a[i]>s时要分解为s 和a[i]-s
首尾的默认为分解成了0,a[1] 和a[n]和0——a[i] 对答案产生的影响只在y[i-1]*x[i] + y[i]*x[i+1]这一部分,再变一下就是y[i-1]*x[i]+(s-x[i])*x[i+1],由a[i]的大小决定x[i]可以由上面两种情况的其中一种的两个不同的值
定义一个数组d[i][4]={0 , s , a[i]-s , a[i]}表示的a[i]分解出来的yi的4种情况,
状态定义:f[i][j]表示前i项在a[i]分解出来的y是第j种情况时的最小和
状态转移: f[i][k]=min(f[i][k] , f[i-1][j] + ( d[i-1][j] * ( a[i]-d[i][k] ))) 这里有三重循环,第i个数的状态要从上一个数的所有状态都尝试转移过来.
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int,int>PII;
const int N=2e5+10,mod=1e18;
int f[N][4];
int d[N][4];
int a[N];
int n,t,s;
signed main(){
scanf("%lld",&t);
while(t--)
{
scanf("%lld%lld",&n,&s);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
d[i][0]=0;
d[i][1]=s;
d[i][2]=a[i]-s;
d[i][3]=a[i];
}
for(int i=0;i<=n;i++)
f[i][0]=f[i][1]=f[i][2]=f[i][3]=1e18;
f[1][3]=0; //第一个刷默认分解为0,a[1],所以y1=a[1] ,默认从第三种情况开始
for(int i=2;i<=n;i++)
for(int j=0;j<4;j++)
for(int k=0;k<4;k++)
{
if(i!=n) //最后一个数默认分解为a[n],0
{
if(a[i]>s&&(k==0||k==3)) continue;
if(a[i]<s&&(k==1||k==2)) continue;
}
f[i][k]=min(f[i][k],f[i-1][j]+d[i-1][j]*(a[i]-d[i][k]));
}
printf("%lld\n",f[n][0]);
}
return 0;
}