poj 1036 Gangster -- 区间型DP解法
2012-05-31 23:57
302 查看
/* * poj 1036 Gangster 题目大意: N个抢匪陆续的进入一家餐馆,餐馆的们有K个开放度,每个绑匪只有在开放度适合 自己时才能进入,并给餐馆带来相应收益P。问通过控制门的开、关(一个时间单位 门只能开1、关1或者保持不变),餐馆能获得的最大收益是多少? 数学模型: 1、假设所有歹徒已经按照进入时刻,按照从先到后的顺序进行了排序。-- 排序是必要步骤,后续讲到 2、s(i,j)表示介于第i个活动和第j个歹徒之间,所有同时与与歹徒g[i]和g[j]兼容的 歹徒组成的集合。 f(i,j)表示通过控制门的开关使得集合s(i,j)中歹徒进入餐馆所能获得的最大收益。 3、兼容性的数学表示: -- 歹徒a和b兼容(不失一般性,假设T[a]>T[b]) T[a]-T[b] >= |S[a]-S[b]| 4、为了表示题目的原始要求,引入辅助歹徒g[0]和g[n+1]。 4.1、g[0]的进餐馆时刻T[0]=0,需要门的开放度O[0]=0 4.2、g[n+1]的进餐馆时刻T[n+1]=6000,需要门的开放度O[n+1]=0 -- g[n+1]的取值后续会解释 则所有的歹徒均与这两个辅助歹徒兼容,最后求出f(0, n+1)即是所求。 5、由以上4点,得出由如下递归式成立: |-- 0 if s(i,j)={NULL} -- 集合s(i,j)为空 f(i, j) = ---| |-- max{f(i,k)+P[k]+f(k,j) | g[k] belong_to s(i,j)} 针对每个选择g[k]会将原问题划分成两个子问题f(i,k)和f(k,j),但是子问题的解 能否构成原问题的解尚需考证。如果不能,则该递归式毫无意义。如果能,需要进 一步考虑,这些子问题是否相互重复 -- 使用动态规划的第二个特征。 第1步的排序,保证了两个子问题的解可以合并成原问题的解。如果不排序,则有如 下反例: G | T K --|-------- x | 8 1 | k | 1 1 | y | 10 10 上例中歹徒g[x] belong_to s(i,k)、g[y] belong_to s(k,j),但是明显g[x]与g[y] 互不兼容,因此他们构成的解也肯定不是原问题的正确解! 6、证明: 按照进入时刻排序后,两个子问题f(i,k)和f(k,j)的解可以构成原问题的解。 证明: 假设存在歹徒g[x] belong_to s(i,k)和g[y] belong_to s(k,j),则根据兼容 性,有如下表达式成立: I) T[k]-T[x] >= |S[k]-S[x]| II) T[y]-T[k] >= |S[y]-S[k]| 合并表达式I、II得: III) T[y]-T[x] >= |S[y]-S[k]| + |S[k]-S[x]| -->绝对值的和 >= 和的绝对值 >= |S[y]-S[x]| 因此可以得出结论: 集合s(i,k)与集合s(k,j)完全兼容,因此{s(i,k) U g[k] U s(k,j)} 是原问题的一个解。 问题: 1、为什么要排序? 不排序该递归式就不成立了吗? 2、T[n+1]需要设置成6000吗? 小点行不行? 解答: 1、排序保证了两个子问题f(i,k)与f(k,j)的解可以构成原问题的一个解,是递归式成立的基础。 不排序,递归式不成立,上面已经给出了反例。 2、因为原题目已经明确说明了K最大取100,因此T[n+1]设置成大于等于3100的值均可。 目的是保证,歹徒g[n+1]一定与所有歹徒兼容,即满足兼容的数学表达式。 */ #include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> namespace { using namespace std; typedef struct GANGSTER { int t, k, p; }GANGSTER_S; int gangster_comp(const void *pOP1, const void *pOP2) { GANGSTER_S *pG1 = (GANGSTER_S *)pOP1; GANGSTER_S *pG2 = (GANGSTER_S *)pOP2; if (pG1->t < pG2->t) return -1; if (pG1->t == pG2->t) return 0; if (pG1->t > pG2->t) return 1; } inline bool gangster_compatible(const GANGSTER_S &G1, const GANGSTER_S &G2) { return abs(G1.t-G2.t) >= abs(G1.k-G2.k); } const int N_MAX = 100; GANGSTER_S G[N_MAX+1]; // 歹徒存储 int f[N_MAX+2][N_MAX+2]; // f(i, j) int n; // 实际有效的歹徒个数 int gangster() { qsort(&G[1], n, sizeof(G[1]), gangster_comp); // 排序,保证子问题的解可以构成原问题的解 G[n+1].t = 6000; // 辅助歹徒,g[n+1] for (int l=2; l<=n+1; l++) // 每次处理的长度 { for (int i=0; i<=n; i++) { int j=i+l; // 当前行处理的列序数 if (j<=n+1) // 防止越界 { for (int k=i+1; k<j; k++) // 枚举k值,查找兼容的歹徒 { // 兼容性判断 if (gangster_compatible(G[i], G[k]) && gangster_compatible(G[k], G[j])) { if (f[i][k]+G[k].p+f[k][j] > f[i][j]) f[i][j] = f[i][k]+G[k].p+f[k][j]; } } } } } return f[0][n+1]; } } int main() { int N, K, T; scanf("%d%d%d", &N, &K, &T); for (int i=1; i<=N; i++) { scanf("%d", &G[i].t); } for (int i=1; i<=N; i++) { scanf("%d", &G[i].p); } for (int i=1; i<=N; i++) { scanf("%d", &G[i].k); } int i=1; n=N; while (i<=n) { // 去除无效的歹徒 if (G[i].k>K || G[i].k>T ||G[i].k>G[i].t) { swap(G[i], G ); n--; continue; } ++i; } printf("%d\n", gangster()); return 0; }
相关文章推荐
- poj 1036 Gangster -- 最长上升子序列解法
- [poj 1014]Dividing的DFS解法解读和DP解法
- POJ 1036 Gangsters (DP)
- poj1036-题目好难理解,理解了就变得简单了许多dp
- poj 1036 Gangsters 简单dp
- [POJ 3311]Hie with the Pie——再谈TSP问题的DP解法
- POJ 1036 Gangster (动态规划)
- poj 2029 Get Many Persimmon Trees 各种解法都有,其实就是瞎搞不算吧是dp
- poj1036 dp
- poj 1141 dp解法
- poj 1036 DP
- poj1036-dp
- poj - 1036 - Gangsters(滚动数组dp)
- poj1036-dp
- POJ1036 Gangsters(DP)
- poj 1036 Gangsters(DP)
- POJ 1036 Gangsters(DP)
- POJ 1170 多重DP 无状态压缩解法
- poj1036 dp
- poj 1036 Gangsters (DP:滚动数组)