题目大意:
题目链接:https://www.cometoj.com/contest/58/problem/C?problem_id=2760
有
n
n
n个物品,每个物品有
a
,
b
a,b
a,b两个属性,每次合并两个相邻的物品
i
,
i
+
1
i,i+1
i,i+1会合并成一个
(
a
i
,
b
i
+
1
)
(a_i,b_{i+1})
(ai,bi+1)物品,但需花费代价
a
i
+
1
×
b
i
a_{i+1}\times b_i
ai+1×bi。在开始前可以选择一段区间里的物品,然他们的两个属性全部乘
k
k
k。求合并后代价的最小值。
思路:
先考虑没有乘
k
k
k的操作,那么显然无论合并的顺序是怎样的,最终结果都相同。因为任意一次合并都没有拆散其它相邻的物品。
所以只需考虑乘
k
k
k的操作使得答案更小。那么设
f
[
i
]
[
0
/
1
/
2
]
f[i][0/1/2]
f[i][0/1/2]表示全部不使用乘
k
k
k的代价、第
i
i
i个物品一定使用乘
k
k
k的最小代价、前
i
−
1
i-1
i−1个物品使用了乘
k
k
k,第
i
i
i个物品不使用乘
k
k
k的最小代价。
设
c
[
i
]
c[i]
c[i]为本次代价,那么转移方程显然
f
[
i
]
[
0
]
=
f
[
i
−
1
]
[
0
]
+
c
[
i
]
f[i][0]=f[i-1][0]+c[i]
f[i][0]=f[i−1][0]+c[i]
f
[
i
]
[
1
]
=
m
i
n
(
f
[
i
−
1
]
[
0
]
+
c
[
i
]
×
k
,
f
[
i
−
1
]
[
1
]
+
c
[
i
]
×
k
×
k
)
f[i][1]=min(f[i-1][0]+c[i]\times k,f[i-1][1]+c[i]\times k\times k)
f[i][1]=min(f[i−1][0]+c[i]×k,f[i−1][1]+c[i]×k×k)
f
[
i
]
[
2
]
=
m
i
n
(
f
[
i
−
1
]
[
2
]
+
c
[
i
]
,
f
[
i
−
1
]
[
1
]
+
c
[
i
]
×
k
)
f[i][2]=min(f[i-1][2]+c[i],f[i-1][1]+c[i]\times k)
f[i][2]=min(f[i−1][2]+c[i],f[i−1][1]+c[i]×k)
最后取个 m i n min min即可。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=100010;
int n;
ll f[N][3],a[N],b[N],c[N],k;
int main()
{
scanf("%d",&n);
scanf("%lld",&k);
for (int i=1;i<=n;i++)
{
scanf("%lld%lld",&a[i],&b[i]);
if (i>=2) c[i-1]=a[i]*b[i-1];
}
memset(f,0x3f3f3f3f,sizeof(f));
f[0][0]=f[0][1]=f[0][2]=0;
for (int i=1;i<n;i++)
{
f[i][0]=f[i-1][0]+c[i];
f[i][1]=min(f[i-1][0]+c[i]*k,f[i-1][1]+c[i]*k*k);
f[i][2]=min(f[i-1][2]+c[i],f[i-1][1]+c[i]*k);
}
printf("%lld\n",min(f[n-1][0],min(f[n-1][1],f[n-1][2])));
return 0;
}