uvaLive 3222 Joke with Turtles 带权区间调度、覆盖问题 等价转换+线性动归


带权的区间调度问题,贪心似乎不好解决,一般的方法是动态规划


题目:点我



细节分析: 对于一个乌龟说的话,前面有a个乌龟,后面有b个乌龟,意为 该乌龟可能是第[a+1,n-b]名。



题目解法:


一个区间表示了一个名次,区间的长度表示这个名次有多少人。


给区间附上权值,如果有x个人的名次为区间[a,b],那么区间[a,b]的权值val为min(b-a+1,x)。


代表区间[a,b]最多可能有val个人说真话。


先求最多有多少人说真话。


说真话的最多人数 等价于在若干种区间内选取互不重叠的区间集合,使的权值和最大。


细节理解:


假如一个区间的长度为5,而实际上只出现了1个这样的区间,可以理解为1个人说了真话,该区间的其他人都说了假话。


假如一个区间的长度为5,而实际上出现了9个这样的区间,可以理解为5个人说了真话,其余人说了假话。


假如两个区间重叠,比如[1,4]和[4,7],那么不能同时选取,因为[1,4]意为前面4个人同名次,后面是其他名次,而[4,7]意为第4人到第7人同名次,前面是其它名次,矛盾。


#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#define __len(a,b)  (b-a+1);
using namespace std;
typedef long long ll;
const int INF =0x3f3f3f3f;
const int maxn=1000    ;
int n;
struct Seg
{
    int le,ri,val;
    Seg(){}
    Seg(int le,int ri ):le(le),ri(ri){val=1;}
} a[maxn+5];

vector<int >G[maxn+5];

int dp[maxn+5];
int best[maxn+5];
bool vis[maxn+5];
void work()
{
    dp[0]=0;
    best[0]=-1;
    for(int i=1;i<=n;i++)
    {
        dp[i]=dp[i-1];
        best[i]=-1;
        for(int j=0;j<G[i].size();j++)
        {
            int id=G[i][j];
            int p=a[id].le-1;
            if(dp[p]+a[id].val>dp[i])
            {
                dp[i]=dp[p]+a[id].val;
                best[i]=id;
            }
        }
    }
    printf("%d",n-dp[n]);

    memset(vis,0,sizeof vis);

    int s=n;
    while(s)
    {
        while(s&&best[s]==-1)  s--;

        if(!s)  break;
         int id=best[s];

         int le=a[id].le;
         int cnt=a[id].val;
         for(int i=1;cnt&&i<=n;i++)
         {
             if(a[i].le==le&&a[i].ri==s)
             {
                 vis[i]=1;
                 cnt--;
             }
         }

          s=le-1;

    }

    for(int i=1;i<=n;i++)
    {
        if(!vis[i])  printf(" %d",i);
    }
    putchar('\n');

}

void init()
{
    for(int i=1;i<=n;i++)
    {
       G[i].clear();
    }
}
int main()
{
    int x,y;
    while(~scanf("%d",&n))
    {

        init();
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&x,&y);
            int le=x+1;
            int ri=n-y;
            a[i]=Seg(le,ri);
            if(le>ri)  continue;
              bool ok=0;
            for(int j=0;!ok &&j<G[ri].size();j++)
            {
                int id=G[ri][j];
                Seg &now=a[id];

                if(now.le==le&&now.ri==ri)
                {
                    int val=now.val;
                    now.val=min(ri-le+1,val+1 );//我的老天,先写成了le-ri+1
                    ok=1;
                    break;
                }

            }
                if(ok)  continue;
                G[ri].push_back(i);

        }
        work();
    }

   return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值