input:
10
5 0
2 0 1 3 4
5 1
5 3 4 3 5
7 2
7 6 5 4 3 2 1
5 1
1 2 3 4 5
5 2
1 2 3 4 5
4 0
0 1 1 1
5 5
4 3 5 6 4
4 1
0 2 1 0
3 99999
200000 200000 200000
6 8139
7976 129785 12984 78561 173685 15480
output:
0
18
32
11
14
0
16
0
40000000000
2700826806
题意:
给定一个长度为 n 的数组 a ,给定一个数 s ,现要求将 2 <= i <= n - 1 的所有 ai 拆分为 xi ,yi ,并保证 (xi - s)(yi - s) >= 0 ,试问 F = a1 * x2 + y2 * x3 + ... + yn-1 * an 的最小值是多少。
思路:
首先研究以下每个数拆分的方法
显然当 ai < s 时,拆分为 0 ,ai 最优 ,其余情况拆分为 ai , s - ai 最优。
所以可以直接用二维的 dp 处理问题 ,dp[i][k] 表示处理到第 i 个数 ,拆分方式为第 k 种时,当前的最小值。
可以推出动态转移方程:
dp[i][k]=min(dp[i][k],dp[i-1][j]+pre[j]*(a[i]-cur[k]))
其中 j 是上一个数的拆分方式 ,k为当前数的拆分方式, pre 和 cur 分别为上个数和当前数的拆分数组。
第一个数默认拆分为 0 和 a1 ,最后一个数默认拆分为 an 和 0。
具体见代码。
Code
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define endl "\n"
#define pb push_back
#define int long long
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int mod=1e9+7;
const int N=2e5+10;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
int t,n,s;
int dp[N][4];
int a[N];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--)
{
cin>>n>>s;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<4;j++)
{
dp[i][j]=INF;
}
}
dp[1][3]=0;
for(int i=2;i<=n;i++)
{
vector<int> pre({0,s,a[i-1]-s,a[i-1]});
vector<int> cur({0,s,a[i]-s,a[i]});
for(int j=0;j<4;j++)
{
for(int k=0;k<4;k++)
{
if(i!=n)
{
if(a[i]<s&&(k==1||k==2))
continue;
if(a[i]>s&&(k==0||k==3))
continue;
}
dp[i][k]=min(dp[i][k],dp[i-1][j]+pre[j]*(a[i]-cur[k]));
}
}
}
cout<<dp[n][0]<<endl;
}
return 0;
}
准备期末考试,下下周继续更新