ACM-ICPC Live Archive 3222 Joke with Turtles

poj 2168 相同的题目

区间DP

题意:输入n,表示有n个海龟在一条直线上,乌龟可以站在相同的位置(即坐标可以相同),下面n行,每行两个数字,表示第i个乌龟给出的信息,第一个数字表示它前面有多少只乌龟,第二个数字表示它后面有多少个乌龟。并不是每个乌龟的信息都是正确,有些乌龟的信息是假的,或者和别的乌龟信息冲突,你的任务是选出尽量多的乌龟,使他们的信息不冲突,然后输出有多少个乌龟说谎,和那些乌龟的编号,可能有多种情况,只要保证说谎的乌龟数最少,输出哪种情况都可以

 

分析:乌龟可以站在一样的位置,我们给乌龟排名,可以把它们放在不同的位置 例如  1 2 3 3 3 4 4 5 , 虽然有些排名相同,但是放在不同位置,这样方便我们处理

有n个乌龟,所以我们要准备n个位置。对于一个乌龟,它前面和后面分别为a,b人,那么可以知道,它的位置一定在[a+1,n-b]内,至于它确切在哪个位置并不重要,因为这个区间内的乌龟排名相同的。这样我们就转化为,用一个区间来表示乌龟,这是非常重要的一步

然后没读入一个乌龟,我们就在那个区间计数,w[i][j]表示在这个区间内有多少个乌龟,如果乌龟数超过了(j-i+1)那么不能再计数,因为这个区间最多只容纳这些乌龟

想想我们的问题,是找出最少的说慌的乌龟,反过来就是可以共存的乌龟数最多,而乌龟用区间表示了,那不就是变成了选最多的区间?就是这样,而且要满足,选出来的区间不能重叠

 

关于“所选区间不能重叠”,这个东西不好解释,但,这却是一个显然的事实(往往越是显然的越难理解)。可以这样想,我们是允许排名相同的,但是我们已经给每只乌龟排在一个位置了,只允许是排名相同,不能是位置相同的,如果区间重叠,就变成了位置相同,这和我们最开始的定义是相矛盾的

 

这样问题变为,在总区间[1,n]里面,选一些区间,这些区间不重叠,而且每个区间有权值(表示这个区间有多少乌龟,这些乌龟,不就是排名相同的乌龟嘛,只是安排了不同位置,但位置一定在这个区间内),使得所选区间加起来权值最大,只是个典型的区间模型

dp[i]表示[1,i]区间的最大权值和,目标状态为dp[n],另外在dp过程中要记录路径

方程: dp[i] = max { dp[j] + w[j+1][i]  }

 

记录路径是为了输出。

dp完了我们从n沿路径返回,没找到一个前驱,其实可以确定一个区间w[pre+1][now] , 这个区间对应的其实是乌龟,表示这些乌龟背选中了,它们信息不冲突,不说谎,把这些乌龟标记掉

这时候想想我们一开始为什么要给w[i][j]计数,其实就是记录里面的乌龟数,为什么w[i][j]>(j-i+1)后不能再计数,因为可以知道,肯定有一些乌龟是矛盾的,这个区间不能放下这么多乌龟

然后输出没有被选中的乌龟即可

 

程序跑了200ms左右,是因为在选乌龟的时候是直接暴力扫描的,没有做优化

有优化想法欢迎分享

 

#include <cstdio>
#include <cstring>
#include <vector>
#include <utility>
using namespace std;
#define N 1010

int n;
int w[N][N];
int dp[N] , p[N];
struct tur
{
   int a,b,c;
}t[N];

void solve()
{
   memset(dp,0,sizeof(dp));
   memset(p,-1,sizeof(p));

   for(int i = 1; i <= n; i++)
      for(int j = 0; j<n; j++)
         if(dp[j] + w[j+1][i] > dp[i])
         {
            dp[i] = dp[j] + w[j+1][i];
            p[i] = j;
         }

   bool used[N];
   memset(used,false,sizeof(used));
   int pre,now,count,c;
   now = n;
   count = 0;
   while(1) //沿路径返回并标记说了真话的乌龟
   {
      pre = p[now];
      if(pre == -1) break;
      //区间为[pre+1,now]
      c = w[pre+1][now]; //有多少只重叠的乌龟
      for(int i=0; i<n && c; i++)
         if(t[i].a == pre && t[i].b == n-now)
         {
            used[i] = true;
            c--;
            count++;
         }
      now = pre;
   }
   printf("%d",n-count);
   for(int i=0; i<n; i++)
      if(!used[i])
         printf(" %d",i+1);
   printf("\n");
}

int main()
{
   while(scanf("%d",&n)!=EOF)
   {
      memset(w,0,sizeof(w));
      for(int i=0; i<n; i++)
      {
         int a,b;
         scanf("%d%d",&a,&b);
         t[i].a = a; t[i].b = b;
         t[i].c = n - t[i].a - t[i].b;
         w[a+1][n-b]++;
         if(w[a+1][n-b] > t[i].c)
            w[a+1][n-b] = t[i].c;
      }
      solve();
   }
   return 0;
}

 

posted @ 2013-04-22 19:20  Titanium  阅读(416)  评论(0编辑  收藏  举报