HDU 4293 Groups 区间覆盖 区间DP

题目描述:

Description
  After the regional contest, all the ACMers are walking alone a very long avenue to the dining hall in groups. Groups can vary in size for kinds of reasons, which means, several players could walk together, forming a group.
  As the leader of the volunteers, you want to know where each player is. So you call every player on the road, and get the reply like “Well, there are A i players in front of our group, as well as B i players are following us.” from the i th player.
  You may assume that only N players walk in their way, and you get N information, one from each player.
  When you collected all the information, you found that you’re provided with wrong information. You would like to figure out, in the best situation, the number of people who provide correct information. By saying “the best situation” we mean as many people as possible are providing correct information.

Input
  There’re several test cases.
  In each test case, the first line contains a single integer N (1 <= N <= 500) denoting the number of players along the avenue. The following N lines specify the players. Each of them contains two integers A i and B i (0 <= A i,B i < N) separated by single spaces.
  Please process until EOF (End Of File).

Output
  For each test case your program should output a single integer M, the maximum number of players providing correct information.

Sample Input

3
2 0
0 2
2 2
3
2 0
0 2
2 2 

Sample Output

2
2 

Hint
The third player must be making a mistake, since only 3 plays exist.

题目分析:

有n个人,分成若干组,他们都会提供一个信息,在他们组(不是他)之前有多少人,之后有多少人。然而,有些人说的是谎话,你需要判断,在尽可能让多的人说真话的前提下,求出说真话的人数。
简化一下题目,就是在0~n的区间中,你通过在该区间前后长度来确定区间,同时这个区间可以取多次,(对应题目的数个人在同一组)当然这个取这个区间的次数必须是区间长度范围内,(对应一个区间的人数不能超过该区间的最大能容下的人数)。然后就贪心计算不会重复覆盖的区间个数,就是本题答案。

还有DP的做法。

代码如下:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

const int MAXN =510;
using namespace std;


struct node
{
    int l,r;
    int num;
}pp[MAXN];

bool cmp(node a,node b)
{
    if (a.l==b.l) return a.r<b.r;
    else return a.l<b.l;
}

int n;
int main()
{
    while(scanf("%d",&n)!=-1)
    {
        int nn=0;
        for(int i=1; i<=n; i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            if (a+b>=n) continue;//不合法情况,即区间左端点比右端点大的情况
            pp[nn].l=a+1;
            pp[nn].r=n-b;
            pp[nn].num=1;
            nn++;
        }
        sort(pp,pp+nn,cmp);
        if (nn==0) {printf("0\n");continue;}
        int group=0;//初始第一个人在一组
        for(int i=1; i<nn; i++)
        {
            if (pp[i].l==pp[group].l && pp[i].r==pp[group].r)
            {
                pp[group].num++;//在同一组
                pp[group].num=min(pp[group].num,pp[group].r-pp[group].l+1);//在同一组的人数不能比这区间人数更多了
            }
            else
            {
                group++;//新的一组,将group值更新
                pp[group].l=pp[i].l;
                pp[group].r=pp[i].r;
                pp[group].num=pp[group].num;
            }
        }
        int ans=pp[0].num;
        for(int i=1; i<=group; i++)
        {
            int t=0;
            for(int j=0; j<i; j++)
            {
                if (pp[j].r<pp[i].l && pp[j].num>t) t=pp[j].num;
                //前一个条件判断合法性 后一个条件判断是否值得更新
            }
            pp[i].num+=t;//前i组可以取得的最大区间数量
            ans=max(ans,pp[i].num);
        }
        printf("%d\n",ans);
    }
    return 0;
}

这种做法就类似与HDU2037

DP做法:

#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;

int dp[510][510];    
int tt[510][510], an[510];

int main()
{
    int i, j, k;
    int n, a, b;
    while(scanf("%d", &n) != EOF)
    {
        memset(dp, 0, sizeof(dp));
        memset(an, 0, sizeof(an));
        memset(tt, 0, sizeof(tt));

        for(i = 0; i < n; ++i) {
            scanf("%d %d", &a, &b);
            if(a+b < n && tt[a][n-b] < (n-a-b))
                tt[a][n-b]++;
        }

        for(i = 1; i <= n; ++i)
        for(j = 0; j < i; ++j) {
            dp[j][i] = an[j]+tt[j][i];
            an[i] = max(an[i], dp[j][i]);
        }

        printf("%d\n", an[n]);
    }
    return 0;
}

用dp[ i ][ j ] 表示从第 i+1 个人到第 j 个人为一组的时候前j个人中说真话最多的人

用tt[ i ][ j ] 表示从第 i 个人到第 j 个人之间站了多少人

用an[ i ]表示到第 i 个人前面说真话的人最多人数

tt[ a ][ b ]的人数不应该多于 n-(b-a+1)

你可能感兴趣的:(dp,贪心,区间覆盖)