• ACMICPC 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;
    }
  • 相关阅读:
    柯里化函数
    函数部分应用Partial application
    001Spark文件分析测试
    001Spring4.2基本环境搭建
    Rectangle 响应按键
    jQuery打印Html页面自动分页
    jquery实现页面局部刷新
    2014 年10个最佳的PHP图像操作库--留着有用
    20 个势头最猛的开发者工具
    20+个很有用的 jQuery 的 Google 地图插件
  • 原文地址:https://www.cnblogs.com/scau20110726/p/3036352.html
Copyright © 2020-2023  润新知