区间覆盖题解-EOJ2799

来源:

题意

给定  \(n\) 个点,\(m\) 个操作,每次操作可以将第 \(L_i\) 个点到第 \(R_i\) 个点覆盖,耗费是 \(W_i\)
问至少覆盖(可以重复覆盖) \(k\) 个点的最小耗费是多少?

题解

  • 定义:Cost[l][r] 为覆盖区间\([l,r]\)的最小代价,cost[l][r]为覆盖区间\([l,r]\)的代价

  • 考虑dp状态定义:设dp[i][j]为覆盖前i个点中的j个点所需的最小代价。

  • 考虑转移

  • 考虑一个操作\(L,R,W\),覆盖区间长度为\(len = R-L+1\)。考虑操作之前状态为,前\(L-1\)个点的状态\(dp[L-1,x]\) ,操作后则多覆盖\(len\)个点,花费代价\(Cost[L,R]\) 转移到状态\(dp[R,x+len]\)

\[dp[R,x] = \min(dp[R,x],dp[L-1][x-len]+Cost[L,R]) \]

  • 考虑\(Cost[L,R]\)的计算,即覆盖区间\([L,R]\)所需的最小代价。
  • 由于区间可重复覆盖,前\(R\) 个数中覆盖区间\([L,R]\)所需的最小代价为包含该区间的操作区间代价的最小值。

\[Cost[L][R] = \min_{L\le l\le R}cost[l][R] \]

  • 故状态转移方程为

\[dp[i][j] = \min(dp[i][j],\quad\min_{L\le l\le i} dp[L-1][j]+cost[l][i]) \]

  • 最终答案为

\[ans = \min_{k\le i\le n} dp[n][i] \]

AC代码

#include <bits/stdc++.h>
#define ll long long
//#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);
#define endl '\n' 
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
using namespace std;
//ifstream fin("input.txt");
//ofstream fout("output.txt");
//#define cin fin
//#define cout fout

int n,m,k;
const int N = 305,M=1e5+10;
ll dp[N][N];
ll cost[N][N];

signed main()
{
    IOS
    int t;cin>>t;
    for(int cs=0;cs<t;cs++) {
        cin>>n>>m>>k;
        for(int i=0;i<=n+5;i++) {
            for(int j=0;j<=n+5;j++) {
                dp[i][j]=infll;
                cost[i][j]=infll;
            }
        }
        dp[0][0]=0;
        for(int i=1;i<=m;i++) {
            int l,r,w;cin>>l>>r>>w;
            cost[l][r] = min(cost[l][r],w*1ll);
        }

        for (int len=n; len>=1; len--) {
            for (int l=1; l+len-1<=n;l++) {
                int r = l+len-1;
                if (l-1>=1) cost[l][r] = min(cost[l][r],cost[l-1][r]);
            }
        }


        for(int i=1;i<=n;i++) {
            for(int j=0;j<=n;j++) {
                dp[i][j] = dp[i-1][j];
            }
            for(int l=1;l<=i;l++) {
                if(cost[l][i]==infll) continue;
                int len = i-l+1;
                for(int j=len;j<=n;j++) {
                    if(l-1>=0)
                        dp[i][j] = min(dp[i][j],dp[l-1][j-len]+cost[l][i]);
                }

            }
        }

        cout<<"case #"<<cs<<":\n";
        ll ans = infll;
        for(int i=k;i<=n;i++) ans = min(ans,dp[n][i]);
        if(ans==infll) cout<<-1<<endl;
        else cout<<ans<<endl;

    }
    //fin.close(),fout.close();
    return 0;
}
posted @ 2025-10-30 13:29  NightRainLone  阅读(31)  评论(0)    收藏  举报