poj 1036 Gangster -- 区间型DP解法

来源:互联网 发布:五轴编程 编辑:程序博客网 时间:2024/06/05 20:59
/* *  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]);            n--;                        continue;        }        ++i;    }    printf("%d\n", gangster());        return 0;}


原创粉丝点击