1. 程式人生 > >【2017ccpc final G - Alice’s Stamps HDU - 6249 】【dp】【01揹包變形】【取k個區間使得覆蓋範圍最大】

【2017ccpc final G - Alice’s Stamps HDU - 6249 】【dp】【01揹包變形】【取k個區間使得覆蓋範圍最大】

【連結】

acm.hdu.edu.cn/showproblem.php?pid=6249

【題意】

給你m個區間,要求你選出k個區間,使得區間並的覆蓋範圍最大

1≤T≤100
1≤K≤M
1≤N,M≤2000
1≤Li≤Ri≤N

【思路】

一開始我們得出錯誤的dp轉移:

先按右端點排序,dp[i][j]表示前i個區間集合,當前已經選了j個的最大的覆蓋範圍,但是顯然需要再列舉是哪個右端點轉移過來的。顯然是O(n^3)的複雜度顯然不行,但是我們錯誤地記錄了轉移到的最大的貢獻來自於哪個右端點,其實這樣是錯誤的。哎,思維不夠嚴謹。

正確的做法是:改變dp狀態設計。可以觀察到,n的範圍只有2000,那麼這個條件肯定是要用的。我們設計dp[i][j]表示當前位於值為i的點,並且選了j個區間的最大dp值,顯然,對於一個點,我們會選擇能覆蓋這個點的最遠的區間右端點,維護一下這個,對於每個點,選與不選轉移即可

 

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e3 + 5;
int L[maxn], R[maxn];
int dp[maxn][maxn];
int up[maxn];

int main() {
	int t;
	scanf("%d", &t);
	int ca = 0;
	while (t--) {
		memset(dp, 0, sizeof(dp));
		memset(up, 0, sizeof(up));
		int n, m, k;
		scanf("%d%d%d", &n, &m, &k);
		for (int i = 0; i < m; i++) {
			int x, y;
			scanf("%d%d", &x, &y);
			for (int j = x; j <= y; j++) {
				up[j] = max(up[j], y);
			}
		}
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= k; j++) {
				dp[i][j] = max(dp[i][j], dp[i - 1][j]);
				if (up[i]) {
					dp[up[i]][j] = max(dp[up[i]][j], dp[i - 1][j-1] + up[i] - i + 1);
				}
			}
		}
		printf("Case #%d: %d\n", ++ca, dp[n][k]);
	}
}