题目大意
一条直线上有 n(1≤n≤300) 个点可以用来修洞,有 m(1≤m≤105) 个建筑公司,每个建筑公司 i(1≤i≤m) 需要 ci(1≤ci≤109) 的价钱修理包含于区间 [Li, Ri](1≤Li≤Ri≤n) 中的一段连续的洞。问至少修建 k(1≤k≤n) 个洞需要花费的最小价钱
做法分析
初一看像是求最大流,但是仔细一想,如果是网络流的话,建好的网络太大,绝对TLE,而且貌似这还不好建图
果断放弃,想想DP怎么做
可以这样想:f[i][p] 表示前 i 个洞中修建了 p 个洞需要花费的最小价钱,转移方程为:
f[i][p] = min{ f[j][p-i-j]+c[j][i] },1≤j≤i-1
其中 c[i][j] 表示 [i+1, j] 这段连续的区间中的洞用一个公司修需要花费的最小代价。
这个DP是 n3 的,不会T,现在就想想 c[i][j] 怎么求
刚开始的时候,我们得到每个公司修建的最大区间,先初始化这些区间
然后可以这样转移:
这样就求出所有的区间 [i, j] 用一个公司修建需要花费的最小代价了
参考代码
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 5 using namespace std; 6 7 typedef long long LL; 8 const int N=306; 9 const LL INF=(1LL)<<60; 10 11 LL c[N][N], f[N][N], ans; 12 int n, m, Min; 13 14 int main() 15 { 16 scanf("%d%d%d", &n, &m, &Min); 17 for(int i=0; i<=n; i++) 18 for(int j=0; j<=n; j++) c[i][j]=f[i][j]=INF; 19 for(int i=0, a, b; i<m; i++) 20 { 21 LL d; 22 scanf("%d%d%I64d", &a, &b, &d); 23 if(a>b) swap(a, b); 24 c[a-1][b]=min(c[a-1][b], d); 25 } 26 for(int i=0; i<=n; i++) 27 for(int j=n; j>=i; j--) 28 { 29 c[i+1][j]=min(c[i+1][j], c[i][j]); 30 c[i][j-1]=min(c[i][j-1], c[i][j]); 31 } 32 f[0][0]=0LL; 33 for(int i=1; i<=n; i++) 34 for(int j=0; j<=i; j++) 35 { 36 f[i][j]=f[i-1][j]; 37 for(int k=0; k<i; k++) 38 f[i][j]=min(f[i][j], f[k][j-i+k]+c[k][i]); 39 } 40 ans=INF; 41 for(int i=Min; i<=n; i++) ans=min(ans, f[n][i]); 42 if(ans==INF) ans=-1LL; 43 printf("%I64d\n", ans); 44 return 0; 45 }