现在的位置: 首页 > 综合 > 正文

Codeforces Ilya and Roads

2011年05月26日 ⁄ 综合 ⁄ 共 1388字 ⁄ 字号 评论关闭

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;
}

 

抱歉!评论已关闭.