感受
变菜了,居然没有想出来变菜了,居然没有想出来变菜了,居然没有想出来
思路
n3暴力Dp应该很容易想的,这里不再赘述n^3暴力Dp应该很容易想的,这里不再赘述n3暴力Dp应该很容易想的,这里不再赘述
设f[i][j]为前i张邮票使用不超过j个集合获得的最大种类数设f[i][j]为前i张邮票使用不超过j个集合获得的最大种类数设f[i][j]为前i张邮票使用不超过j个集合获得的最大种类数
状态转移分析:状态转移分析:状态转移分析:
f[i][j]如何由f[1..i?1][]更新呢?f[i][j]如何由f[1..i-1][]更新呢?f[i][j]如何由f[1..i?1][]更新呢?
两者差距在于现在多了第i种邮票!两者差距在于现在多了第i种邮票!两者差距在于现在多了第i种邮票!
(1)如果当前不使用任何集合(1)如果当前不使用任何集合(1)如果当前不使用任何集合
f[i][j]=f[i?1][j]f[i][j] = f[i-1][j]f[i][j]=f[i?1][j]
即不管现在有没有第i种邮票,我都不选它,所以两者结果相同即不管现在有没有第i种邮票,我都不选它,所以两者结果\\相同即不管现在有没有第i种邮票,我都不选它,所以两者结果相同
(2)如果当前使用一个集合,即作S(2)如果当前使用一个集合,即作S(2)如果当前使用一个集合,即作S
a.如果S的左端点大于i,对f[i][j]结果肯定无影响a.如果S的左端点大于i,对f[i][j]结果肯定无影响a.如果S的左端点大于i,对f[i][j]结果肯定无影响
因为,此时大于i的邮票还没出现因为,此时大于i的邮票还没出现因为,此时大于i的邮票还没出现
b.如果S的右端点小与i,此更新答案必<=f[i?1][j]b.如果S的右端点小与i,此更新答案必<=f[i-1][j]b.如果S的右端点小与i,此更新答案必<=f[i?1][j]
为什么呢?因为现在的选择并没有涉及到第i种邮票,所以用f[i?1][j]答案更优为什么呢?因为现在的选择并没有涉及到第i种邮票,所以\\用f[i-1][j]答案更优为什么呢?因为现在的选择并没有涉及到第i种邮票,所以用f[i?1][j]答案更优
c.除了a与b情况,那么S的左端点<=i,S的右端点>=ic.除了a与b情况,那么S的左端点<=i,S的右端点>=ic.除了a与b情况,那么S的左端点<=i,S的右端点>=i
也就是从众多包括i的集合中选择一个最优的,其实看下图即可知道最优的是左端点距离i最远且与i有交集的也就是从众多包括i的集合中选择一个最优的,其实看下图\\即可知道最优的是左端点距离i最远且与i有交集的也就是从众多包括i的集合中选择一个最优的,其实看下图即可知道最优的是左端点距离i最远且与i有交集的
对于此情况,用dp[minl?1][j?1]+i?minl+1更新即可对于此情况,用dp[minl - 1][j-1]+i-minl+1更新即可对于此情况,用dp[minl?1][j?1]+i?minl+1更新即可
证毕!是不是很简单,我居然想了那么久!证毕!是不是很简单,我居然想了那么久!证毕!是不是很简单,我居然想了那么久!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e3 + 10;
int f[maxn][maxn];
int lef[maxn], l, r;
int n, m, k;
int main(){
int t, kase = 0;scanf("%d", &t);while(t--){
scanf("%d%d%d", &n, &m, &k);for(int i = 1; i <= n; i++) lef[i] = n + 1;for(int i = 1; i <= m; i++){
scanf("%d%d", &l, &r);for(int j = l; j <= r; j++){
lef[j] = min(lef[j], l);}}for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
f[i][j] = f[i - 1][j];if(lef[i] <= i){
f[i][j] = max(f[i][j], f[lef[i] - 1][j - 1] + i - lef[i] + 1);}}}printf("Case #%d: %d\n", ++kase, f[n][k]);}return 0;
}