DP方程——Coloring Trees ( Codeforces Round #369 (Div. 2) C )

  • 题目链接:
    http://codeforces.com/contest/711/problem/C

  • 分析:
    给出n棵树和m种颜色以及beauty值k,再给出n棵树的初始颜色,0就是没有涂色。再给出给第i棵树涂上第j种颜色需要的花费。beauty值的计算为树颜色集合的数目,比如给出7棵树颜色为2, 1, 1, 1, 3, 2, 2, 3, 1, 3,那么它的连续集合为{2}, {1, 1, 1}, {3}, {2, 2}, {3}, {1}, {3}一共7个集合,所以beauty值为7。题目要求怎么涂色才能使得达到目标beauty值的花费最小,输出最小花费,若不能达到,则输出-1。

  • 题解:
    这是一道很明显的DP,我们可以设DP[i][j][k]来存储 用第i种颜色涂染第j棵树并且beauty值为k时的花费。

    转移方程可以这么想: 首先beauty值的计算只与当前涂色和前一棵树的涂色有关,若两者不同则当前beauty值增加,否则beauty值不变。然后给树涂色这一步操作看似会影响到之后涂色的beauty值,但是不会对之后状态涂色的花费产生影响,所以并没有后效性。

    转移方程:

if(tree[i] == 0) //当前树没有被涂色
{
    for(int a = 1; a <= m; a++)
    {
        DP[a][i][j] = min(DP[a][i][j], DP[a][i-1][j] + cost[i][a]); //先与上一棵树相同涂色的比较
        for(int b = 1; b <= m; b++) //再遍历不同涂色的
        {
            if(b != a) DP[a][i][j] = min(DP[a][i][j], DP[b][i-1][j-1] + cost[i][a]);
        }
    }
}
else   //当前树已经被涂色
{
    DP[tree[i]][i][j] = min(DP[tree[i]][i][j], DP[tree[i]][i-1][j]);   //相同涂色
    for(int b = 1; b <= m; b++) //遍历不同涂色
    {
        if(b != tree[i]) DP[tree[i]][i][j] = min(DP[tree[i]][i][j], DP[b][i-1][j-1]);
    }
}
  • AC代码:
    这里写图片描述
/*************************************************************************
    > File Name: test.cpp
    > Author: Akira 
    > Mail: qaq.febr2.qaq@gmail.com 
 ************************************************************************/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <cmath>
#include <vector>
#include <set>
#include <list>
#include <ctime>
typedef long long LL;
typedef unsigned long long ULL;
typedef long double LD;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) ((a)*(a))
using namespace std;

#define MaxN 100000
#define MaxM MaxN*10
const LL INF = LL(1e18);
#define bug cout<<88888888<<endl;


int n,m,k; 
int tree[110];
LL ans;

LL cost[110][110];

LL DP[110][110][110]; //用第i种颜色涂第j颗树,beauty为k的花费




LL solve()
{
    ans = INF;
    if(tree[1]!=0)
    {
        DP[tree[1]][1][1] = 0;
    }
    else
    {
        for(int i=1;i<=m;i++)
        {
            DP[i][1][1] = cost[1][i];
        }
    }
    for(int i = 2; i <= n; i++)
    {
        for(int j = 1; j <= k; j++)
        {
            if(tree[i] == 0)
            {
                for(int a = 1; a <= m; a++)
                {
                    DP[a][i][j] = min(DP[a][i][j], DP[a][i-1][j] + cost[i][a]);
                    for(int b = 1; b <= m; b++)
                    {
                        if(b != a) DP[a][i][j] = min(DP[a][i][j], DP[b][i-1][j-1] + cost[i][a]);
                    }
                }
            }
            else
            {
                DP[tree[i]][i][j] = min(DP[tree[i]][i][j], DP[tree[i]][i-1][j]);
                for(int b = 1; b <= m; b++)
                {
                    if(b != tree[i]) DP[tree[i]][i][j] = min(DP[tree[i]][i][j], DP[b][i-1][j-1]);
                }
            }
        }
    }
    for(int i = 1; i <= m; i++)
    {
        ans = min(ans, DP[i][n][k]);
    }
    if(ans >= INF) return -1;
    else return ans;
}

int main()
{
    while( ~scanf("%d%d%d", &n, &m, &k))
    {
        for(int i=0;i<=m;i++)
            for(int j=0;j<=n;j++)
                for(int l=0;l<=k;l++)
                    DP[i][j][l] = INF;

        for(int i=1;i<=n;i++)
            scanf("%d", &tree[i]);

        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                scanf("%lld", &cost[i][j]);
            }
        }

        cout << solve() << endl;
    }

    //system("pause");
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值