前置知识
预设性 DP
解法
考虑统计每个数单独的贡献,然后进行预设性 DP。
设 \(f_{i,j}\) 表示当前填了 \([1,i]\) 时有 \(j\) 个连续段的最小权值,边界为 \(f_{0,0}=0\)。
对 \(i(i \ne s,i \ne e)\) 填入的位置进行分讨。
- 新开一段
- 后面填入的数都比 \(i\) 大(如果存在后面填入的数的话,即 \(j>[i>s]+[i>t]\)),故 \(f_{i,j}=\min(f_{i,j},f_{i-1,j-1}-2x_{i}+b_{i}+d_{i})\)。
- 连接到某个连续段的前后
- 连接到某个连续段的前面(如果可以在前面的话,即 \(j>[i>s]\))时,后面填入的数(在 \(i\) 左面)比 \(i\) 大,而 \(i\) 右面的数比 \(i\) 小,故 \(f_{i,j}=\min(f_{i,j},f_{i-1,j}+b_{i}+c_{i})\)。
- 连接到某个连续段的后面(如果可以在后面的话,即 \(j>[i>t]\))时,后面填入的数(在 \(i\) 右面)比 \(i\) 大,而 \(i\) 左面的数比 \(i\) 小,故 \(f_{i,j}=\min(f_{i,j},f_{i-1,j}+a_{i}+d_{i})\)。
- 连接两个连续段
- \(i\) 比旁边的两个数都要大,故 \(f_{i,j}=\min(f_{i,j},f_{i-1,j+1}+2x_{i}+a_{i}+c_{i})\)。
当 \(i=s\) 时只可能放到首端,可行的转移操作只有新开一段和连接到某个连续段的前面且前面不会再有数填,故 \(f_{i,j}=\min(f_{i-1,j-1}-x_{i}+d_{i},f_{i-1,j}+x_{i}+c_{i})\);当 \(i=e\) 时只可能放到末端,可行的转移操作只有新开一段和连接到某个连续段的后面且后面不会再有数填,故 \(f_{i,j}=\min(f_{i-1,j-1}-x_{i}+b_{i},f_{i-1,j}+x_{i}+a_{i})\)。
最终有 \(f_{n,1}\) 即为所求。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define sort stable_sort
#define endl '\n'
ll f[5010][5010],x[5010],a[5010],b[5010],c[5010],d[5010];
int main()
{
ll n,s,e,i,j;
cin>>n>>s>>e;
for(i=1;i<=n;i++)
{
cin>>x[i];
}
for(i=1;i<=n;i++)
{
cin>>a[i];
}
for(i=1;i<=n;i++)
{
cin>>b[i];
}
for(i=1;i<=n;i++)
{
cin>>c[i];
}
for(i=1;i<=n;i++)
{
cin>>d[i];
}
memset(f,0x3f,sizeof(f));
f[0][0]=0;
for(i=1;i<=n;i++)
{
for(j=1;j<=i;j++)
{
if(i==s)
{
f[i][j]=min(f[i][j],f[i-1][j-1]-x[i]+d[i]);
f[i][j]=min(f[i][j],f[i-1][j]+x[i]+c[i]);
}
else
{
if(i==e)
{
f[i][j]=min(f[i][j],f[i-1][j-1]-x[i]+b[i]);
f[i][j]=min(f[i][j],f[i-1][j]+x[i]+a[i]);
}
else
{
if(j>(i>s)+(i>e))
{
f[i][j]=min(f[i][j],f[i-1][j-1]-2*x[i]+b[i]+d[i]);
}
if(j>(i>s))
{
f[i][j]=min(f[i][j],f[i-1][j]+b[i]+c[i]);
}
if(j>(i>e))
{
f[i][j]=min(f[i][j],f[i-1][j]+a[i]+d[i]);
}
f[i][j]=min(f[i][j],f[i-1][j+1]+2*x[i]+a[i]+c[i]);
}
}
}
}
cout<<f[n][1]<<endl;
return 0;
}
标签:5010,填入,min,题解,后面,CF704B,Ant,long,define
From: https://www.cnblogs.com/The-Shadow-Dragon/p/18399200