C. Coloring Trees (三维DP)

8 篇文章 0 订阅
  • 题目链接: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(cost, LL_INF, sizeof(LL_INF));
    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);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值