- 题目链接:http://codeforces.com/contest/711/problem/C
- 题意:给你一列树,有n颗。给你m种颜色,这一列树中有些已经被涂好颜色了,但有些没有涂颜色。现在要给这些没有涂颜色的树上色,对于第i颗树,要涂第j种颜色,需要消耗p[i][j]的颜料。定义这一列树的美丽值为连续相同颜色段的段数。问使得美丽值为k的所需消耗的颜料的最小值。
- 算法:三维DP
- 思路:数据量为100,三重循环可行。设dp[i][j][k]表示前i颗树,美丽值为j,第i颗树颜色为k时的最小消耗量。则状态转移方程为:dp[i][j][k] = min( my, dp[i-1][j][k] ) + cost[i][k]。
- my = min( dp[i-1][j-1][非k])。要实现my则需要记录最小和倒数第二小的dp[i-1][j-1],当最小的k和当前k相同时,换成第二小的。
#include <bits/stdc++.h>
#define pi acos(-1)
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const LL LL_INF = 0x3f3f3f3f3f3f3f3f;
const int maxn = 100 + 10;
const int mod = 1e9 + 7;
int c[maxn];
LL my=0;
LL cost[maxn][maxn], dp[maxn][maxn][maxn];
pair<long long, long long > m1[maxn][maxn], m2[maxn][maxn];
void init()
{
memset(dp, LL_INF, sizeof(dp));
memset(m1, LL_INF, sizeof(m1));
memset(m2, LL_INF, sizeof(m2));
m1[0][0] = { 0, -1};
m2[0][0] = { 0, -1};
}
int main()
{
init();
int n, m, s;
scanf("%d%d%d", &n, &m, &s);
for(int i=1; i<=n; i++) scanf("%d", &c[i]);
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
scanf("%I64d", &cost[i][j]);
if(c[i]) cost[i][c[i]]=0;
}
}
for(int i=1; i<=n; i++){
for(int j=1; j<=s && j<=i; j++){
for(int k=1; k<=m; k++){
if(c[i] && c[i]!=k) continue;
my = m1[i-1][j-1].first;
if(m1[i-1][j-1].second == k) my = m2[i-1][j-1].first;
dp[i][j][k] = min(my, dp[i-1][j][k]) + cost[i][k];
if(dp[i][j][k] < m1[i][j].first){
m2[i][j] = m1[i][j];
m1[i][j] = { dp[i][j][k], k};
}
else if(dp[i][j][k] < m2[i][j].first) m2[i][j] = {dp[i][j][k], k};
}
}
}
LL ans = LL_INF;
for(int i=1; i<=m; i++){
ans = min(dp[n][s][i], ans);
}
if(ans == LL_INF) printf("-1\n");
else printf("%I64d\n", ans);
}