[2022编思1062]找出最少动作数

发布时间 2023-04-24 14:31:00作者: 椎名·六花

[2022编思1062]找出最少动作数

题面

有一个栈,这个栈有\(m\)个状态,每个状态记为\(S_i\)每个状态里面有\(n\)种数字,数字\(i\)\(a_i\)个。
考虑从全空,依次经历\(S_1...S_m\),让操作数最小化。

sov

是一个神奇的区间DP。
考虑对于某个区间\(S_i...S_j\),从开始塞进去不用动的数字有\(C_{i,j}\)个,这个区间要进行\(f_{i,j}\)次操作,转移可以是:
\(f_{i,j} = min(f_{i,j}, f_{i,k} + f_{k+1,j} - 2 * C_{i,j})\)
结束

code

#include<stdio.h>
#include<string.h>
#define min(a, b) (a > b ? b : a)
#define MAXN 105
int a[MAXN][MAXN];
int c[MAXN][MAXN];
int f[MAXN][MAXN];
int n, m;
int main()
{
	scanf("%d %d", &m, &n);
	for(int i = 1; i <= m; ++i)
	{
		for(int j = 1; j <= n; ++j)
		{
			scanf("%d", &a[i][j]);
		}
	}
	for(int i = 1; i <= m; ++i)
	{
		int mi;
		for(int k = 1; k <= n; ++k)
		{
			mi = 0x3f3f3f3f;
			for(int j = i; j <= m; ++j)
			{
				mi = min(a[j][k], mi);
				c[i][j] += mi;
			}
		}
	}
	memset(f, 0x3f, sizeof(f));
	for(int i = 1; i <= m; ++i) f[i][i] = 2 * c[i][i];
	for(int len = 1; len <= m; ++len)
	{
		for(int l = 1; l + len - 1 <= m; ++l)
		{
			int r = l + len - 1;
			for(int k = l; k <= r; ++k)
			{
				f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] - 2 * c[l][r]);
			}
		}
	}
	printf("%d", f[1][m]);
}