zoukankan      html  css  js  c++  java
  • Olympiad in Programming and Sports

    题目链接

    https://codeforces.com/contest/730/problem/I

    题目大意

    有 N 个人,每个人编程能力为 xi ,运动能力为 yi

    现要求你选 p 个人参加编程比赛 , s 个人参加运动比赛(每个人只能参加一项比赛)

    使得这 p 个人编程能力和 + 这 s 个人运动能力和最大,问怎么选择

    解题思路

    dp

    我们可以先以一种能力降序排序(假设以 yi)

    那么排完序后,因为是降序的,所以参加运动的人选择的范围肯定是 [1 , p + s]

    而参加编程队的我们就无法确定

    依此我们可以建立dp方程 dp[i][j] 表示前 i 个人有 j 个选择参加编程比赛

    那么转移方程也很显然:

    dp[i][j] = max(①dp[i  - 1][j] , ②dp[i - 1][j - 1] + a[i].x , ③dp[i - 1][j] + a[i].y)

    答案为dp[n][p]

    其中当 i - j > s 时不再进行③的转移,因为此时 [1 , i - i] 已经有大于等于S个人没参加编程比赛 , 也就是这些人是可以参加运动比赛的,而数组是以运动能力降序排序所以前面的人参加运动比赛的收益一定比[i , n]的人高,所以参加运动比赛的人肯定不会从后面选择

    这也是能保证 dp[n][p] 的选择方案里一定有 S 个参加运动比赛的原因(因为是降序的所以当运动人数没满时不参加编程比赛的一定去了运动比赛而不是哪都没去)

    AC_Coder

    #include<bits/stdc++.h>
    #define rep(i,a,n) for (int i=a;i<=n;i++)
    #define int long long
    using namespace std;
    const int N = 3e3 + 10;
    struct node{
        int x , y , id;
        bool operator < (node const & a) const {
            return y > a.y;
        }
    }a[N];
    int dp[N][N] , vis[N];
    pair<int , int>pre[N][N];
    void dfs(int i , int j)
    {
        if(i == 0)  return ;
        if(pre[i][j].second == 1)  vis[i] = 1 ;
        dfs(i - 1 , pre[i][j].first) ;
    }
    signed main()
    {
        int n , p , s; 
        cin >> n >> p >> s;
        rep(i , 1 , n) cin >> a[i].x , a[i].id = i;
        rep(i , 1 , n) cin >> a[i].y;
        sort(a + 1 , a + 1 + n);
        memset(dp , -0x3f3f3f , sizeof(dp));
        dp[0][0] = 0;
        rep(i , 1 , n)
        {
            rep(j , 0 , min(i , p))
            {
                if(i - j <= s && dp[i - 1][j] + a[i].y > dp[i][j]) 
                dp[i][j] = dp[i - 1][j] + a[i].y , pre[i][j] = make_pair(j , 2);
                if(i - j > s && dp[i - 1][j] > dp[i][j])
                dp[i][j] = dp[i - 1][j] , pre[i][j] = make_pair(j , 0);
                if(j - 1 >= 0 && dp[i - 1][j - 1] + a[i].x > dp[i][j])  
                dp[i][j] = dp[i - 1][j - 1] + a[i].x , pre[i][j] = make_pair(j - 1 , 1);
            }
        }
        cout << dp[n][p] << '
    ';
        dfs(n , p) ;
        int cnt = 0 ;
        rep(i , 1 , n)
          if(vis[i])  cout << a[i].id << " ";
        cout << '
    ';
        rep(i , 1 , n)
          if(!vis[i] && cnt < s)  cout << a[i].id << " " , cnt ++ ;
        cout << '
    ';
        return 0;
    }
    凡所不能将我击倒的,都将使我更加强大
  • 相关阅读:
    算法导论--平摊分析之聚集分析
    编译器开发系列--Ocelot语言3.类型名称的消解
    编译器开发系列--Ocelot语言2.变量引用的消解
    编译器开发系列--Ocelot语言1.抽象语法树
    算法导论--散列表的数学分析(精解)链表法
    Linux2.6内核协议栈系列--TCP协议2.接收
    日常‘说说’(回归 原森雨)
    那些玩枪战我特别想听到的声音!
    友链!
    晚安背后的秘密
  • 原文地址:https://www.cnblogs.com/StarRoadTang/p/13026362.html
Copyright © 2011-2022 走看看