Codeforces Ilya and Roads

http://codeforces.com/contest/313/problem/D

区间DP

很好的一道题目,是上周的比赛的题目了现在才补上来

题意:给一个总区间,下面m个小区间,每个小区间有对应的花费,要求用这些小区间去覆盖总区间(允许有重叠),要求覆盖k个单元(不一定连续,只要k个),问最小花费是多少

很典型的区间dp问题,不过数据很大,要想想怎么处理

留意到,总区间长度只有300,但是可供选择的小区间的数目多达10^5个,所以可知很多区间是可以去掉的,相同的区间,我们当然只保留花费最小的,但是除此之外还能怎样再减少小区间的数目呢?

while(m--)

    {

        int x,y;

        ll c;

        cin >> x >> y >> c;

        for(int i=x; i<=y; i++)

            cost[x][i] = min(cost[x][i],c);

    }

这样做的原因,在相同dp的原理后就会明白

但是要注意一点,对于一个小区间【x,y】,只能更新cost[x][x] , cost[x][x+1] , cost[x][x+2] , cost[x][x+3] ……  cost[x][y],去区间的左端不能更改,即不能cost[x+k][]这样的,否则是错误的做法

定义状态:dp[i][j] , 表示从1到i这段连续的区间内,覆盖了k个单元的最小花费

状态转移为

首先初始化 dp[i][j] = dp[i-1][j] , 继承前面i-1个单元的成果

然后  dp[i][j] = min(dp[i][j] , dp[i-k][j-k] + cost[i-k+1][i]);

用文字来表达也很直白,覆盖后面的一段连续的区间 [i-k+1][i] (长度也就是k),那么就已经覆盖了k个,目标是覆盖j个,那么还剩下j-k个,这j-k将在哪里被覆盖,就是在[1,i-k]里面被覆盖

即:【1,i】的区间分成两段[1,i-k]   [i-k+1 , i] , 前面那段,覆盖了j-k个,不一定是连续的,后面那段,覆盖了k个,必定是连续的。 费用就是两段的费用和

所以能理解为什么前面计算cost数组的时候要哪样计算了吧?

 

#include <iostream>

#include <cstdio>

#include <cstring>

using namespace std;

#define N 100010

#define M 310

#define INF 300000000010

typedef long long ll;



ll cost[M][M];

ll dp[M][M];



int main()

{

    int n,m,s;

    cin >> n >> m >> s;

    for(int i=0; i<=n; i++)

        for(int j=0; j<=n; j++)

            dp[i][j] = cost[i][j] = INF;

    while(m--)

    {

        int x,y;

        ll c;

        cin >> x >> y >> c;

        for(int i=x; i<=y; i++)

            cost[x][i] = min(cost[x][i],c);

    }

    dp[0][0] = 0;

    for(int i=1; i<=n; i++)

        for(int j=0; j<=i; j++)

        {

            dp[i][j] = dp[i-1][j];

            for(int k=1; k<=j; k++)

                dp[i][j] = min(dp[i][j] , dp[i-k][j-k] + cost[i-k+1][i]);

        }

    if(dp[n][s] >= INF) dp[n][s] = -1;

    cout << dp[n][s] << endl;

    return 0;

}

 

你可能感兴趣的:(codeforces)