【Comet OJ - Contest #8】C - 符文能量【dp】

题目大意:

题目链接:https://www.cometoj.com/contest/58/problem/C?problem_id=2760
nn个物品,每个物品有a,ba,b两个属性,每次合并两个相邻的物品i,i+1i,i+1会合并成一个(ai,bi+1)(a_i,b_{i+1})物品,但需花费代价ai+1×bia_{i+1}\times b_i。在开始前可以选择一段区间里的物品,然他们的两个属性全部乘kk。求合并后代价的最小值。


思路:

先考虑没有乘kk的操作,那么显然无论合并的顺序是怎样的,最终结果都相同。因为任意一次合并都没有拆散其它相邻的物品。
所以只需考虑乘kk的操作使得答案更小。那么设f[i][0/1/2]f[i][0/1/2]表示全部不使用乘kk的代价、第ii个物品一定使用乘kk的最小代价、前i1i-1个物品使用了乘kk,第ii个物品不使用乘kk的最小代价。
c[i]c[i]为本次代价,那么转移方程显然
f[i][0]=f[i1][0]+c[i]f[i][0]=f[i-1][0]+c[i]
f[i][1]=min(f[i1][0]+c[i]×k,f[i1][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][2]=min(f[i1][2]+c[i],f[i1][1]+c[i]×k)f[i][2]=min(f[i-1][2]+c[i],f[i-1][1]+c[i]\times k)

最后取个minmin即可。


代码:

#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;
}
posted @ 2019-08-10 07:31  全OI最菜  阅读(132)  评论(0编辑  收藏  举报