湖南多校对抗赛

A题

题意

给定n个区间[Li,Ri],然后给一个长度为m的a数组(ai <= ai + 1),求有多少个区间至少覆盖了一个ai

思路

因为ai和区间都具有单调性,所以直接使用单调性即可解决

代码

#include"bits/stdc++.h"
 
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
PII a[N];
int main()
{
   int n;
   cin >> n;
   for(int i = 0 ;i < n ;i ++)
   scanf("%d %d", &a[i].first, &a[i].second);
   
   sort(a, a + n);
   int idx = 0;
   int k;
   cin >> k;
   int res = 0;
   for(int i = 0 ;i < k ;i ++)
   {
       int x;
       scanf("%d", &x);
        while(idx < n && x >= a[idx].first)
        {
            if(x <= a[idx].second)res ++;
            idx ++;
        }
   }
   cout << res;
}

B题

题意

如下面样例, 样例答案为95
在这里插入图片描述
在这里插入图片描述

思路

模拟即可

#include"bits/stdc++.h"
 
using namespace std;
typedef pair<int, int> PII;
const int N = 100;
string ss[N];
long long a[N][N];
int main()
{
   int n , m;
   cin >> n >> m;
   for(int i = 0 ;i  < n ;i ++)
   cin >> ss[i];
   
   for(int i = 0; i < n ;i ++)
   {
       for(int j = 0; j < m ;j ++)
       {
           if(ss[i][j] >= '0' && ss[i][j] <= '9')
           a[i][j] = ss[i][j] - '0';
       }
   }
  
   
   for(int i = n - 1; i >= 1; i --)
   {
       int pre = -1, pos = -1;
       for(int j = 0; j < m ;j ++)
       {
           if(ss[i][j] == '-' || ss[i][j] == '+' || ss[i][j] == '*' || (ss[i][j] >= '0' && ss[i][j] <= '9'))
           {
               if(pre == -1)pre = j;
               else pos = j;
           }
           
           if(pos != -1)
           {
               for(int k = 0; k < m ;k ++)
               {
                   if(ss[i - 1][k] == '-' || ss[i - 1][k] == '+' || ss[i - 1][k] == '*')
                   {
                       if(ss[i - 1][k] == '-')
                       a[i - 1][k] = a[i][pre] - a[i][pos];
                       else if(ss[i - 1][k] == '+')
                       a[i - 1][k] = a[i][pre] + a[i][pos];
                       else a[i - 1][k] = a[i][pre] * a[i][pos];
                       ss[i - 1][k] = '0';
                       break;
                   }
               }
               pos = pre = -1;
           }
       }
    
   }
   
   long long res = 0;
   for(int i = 0; i < m ;i ++)
   res += a[0][i];
   
   cout <<res;
}

G题

题意

一个披萨店有n个订单,每个订单的客户会因为披萨送达的时间而拥有不同的愤怒值,愤怒值的计算: 设外卖送达的时间为t, 且这个客户是第K位收到披萨的客户,则这位客户的愤怒值s = (k - 1 + t) * ai, 每个客户都拥有自己的ai和将披萨送到这位客户手里所需要的时间ti
1.披萨店只有一位外卖员,且每次只能送一份外卖
2.每次送完外卖之后外卖员都必须重新回到店里取下一份外卖
3.现给定每个客户的ti 和 ai 求所有客户愤怒值和的最小值

思路

设客户i 和 客户j,且先送客户i的外卖,再送j的外卖, 如果现在我们先送j 的外卖,再送i 的外卖, 则 j 客户的愤怒值将减少 (2 * ti + 1) * aj, i 客户将增大的愤怒值为 (2 * tj + 1) * ai, 那么如果(2 * ti + 1) * aj > (2 * tj + 1) * ai, 则可以发现j客户减少的愤怒之大于i客户增加的愤怒值,这会减小全局答案,那么我们按(2 * ti + 1) * aj > (2 * tj + 1) * ai 排序即可得到最佳答案.

代码

#include"bits/stdc++.h"
 
using namespace std;
typedef long long ll;
typedef pair<ll, ll> PII;
const int N = 1e5 + 10;
PII a[N];
 
bool cmp(PII x,PII y)
{
    return (2 * y.first + 1) * x.second > (2 * x.first + 1) * y.second;
}
int main()
{
   int n ;
   cin >> n ;
   for(int i = 0 ;i  < n ;i ++)
   scanf("%lld %lld", &a[i].first, &a[i].second);
   
   sort(a , a + n, cmp);
   long long t = 0;
   long long res = 0;
   for(int i = 0; i < n ;i ++)
   {
       t += a[i].first;
       res += (t + i) * a[i].second;
       t += a[i].first;
   }
    cout << res;
}

H题

题意

现在有n个文件和m个包含关系,求最小需要多少个文件才能覆盖所有文件,将所需的文件按升序输出,若有多个答案则输出答案集合中文件编号和最小的答案, 即 若 3, 4, 5 和 1, 2, 3 这两个文件集合都可以覆盖所有文件,则输出1, 2, 3 。因为1 + 2 + 3 < 3 + 4 + 5;

题意

文件包含关系可以看成一条边,则不考虑环的情况下,所有入度为0的点(即不被其他文件包含的文件)即可覆盖所有文件,若存在环,则需要找到环里面编号最小的节点, 那么使用tarjan缩点则可以得到一个DAG(拓扑图), 在DAG上找到所有入度为0的集合中编号最小的点即为答案

代码

#include"bits/stdc++.h"
 
using namespace std;
 
const int N = 1e5 + 10, M = 5e5 + 10;
 
int h[N], e[M] , ne[M], idx, din[N], dout[N];
int stk[N], dfn[N], low[N] , scc_cnt, timestamp, n , m, top , id[N] , cnt[N];
bool st[N];
map<int,int>P;
void add(int a,int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx ++;
}
 
void tarjan(int u)
{
    dfn[u] = low[u] = ++ timestamp; 
    st[u] = true, stk[++ top] = u;
 
    for(int i = h[u]; ~i ; i = ne[i])
    {
        int j = e[i];
        if(!dfn[j])
        {
            tarjan(j);
            low[u] = min(low[u] , low[j]);
        }
        else if(st[j])low[u] = min(low[u] , dfn[j]);
    }
 
 
    if(low[u] == dfn[u])
    {
        ++ scc_cnt; 
        int x;
        do
        {
            x = stk[top -- ];
            id[x] = scc_cnt;
            st[x] = false;
            cnt[scc_cnt] ++;
        }while(x != u);
    }
}
 
int main()
{
    memset(h, - 1, sizeof h);
    cin >> n >> m;
     for(int i = 0; i < m ;i ++)
     {
         int u, v;
         scanf("%d %d", &u, &v);
         add(u, v);
     }
 
     for(int i = 1; i <= n ;i ++)
     if(!dfn[i])
     {
         tarjan(i);
     }
 
     for(int i = 1; i <= n ;i ++)
       for(int j = h[i] ;~j; j = ne[j])
       {
           int u =  e[j];
           int a = id[i] , b = id[u];
           if(a != b)
           dout[b] ++;
       }
       
       for(int i = 1; i <= n ;i ++)
       {
           int k = id[i];
           if(!P.count(k))
           {
               P[k] = i;
           }
           else P[k] = min(P[k], i);
       }
    
    vector<int>res;
    for(int i = 1; i <= scc_cnt; i ++)
    {
        if(!dout[i])
        {
            res.push_back(P[i]);
        }
    }
    
    sort(res.begin(), res.end());
    for(int i = 0; i < res.size(); i ++)
    printf("%d\n", res[i]);
}

I题

题意

有n个人, 每个人都有体重wi 和身高hi,从里面选取K个人排成一列,题目定义了一个美丽值,一个排列的美丽值计算方式为w1 + w2 + …wk - |h1 - h2| -
|h2 - h3| - … |h(k - 1) - hk|, 求美丽值最大的排列

思路

将选取的n个人按身高h排序, 因为序列按h排序则h(i + 1)>= hi, 则|hi - h(i + 1)| = h(i + 1) - hi, 则美丽值为w1 +… wk - (h2 - h1) - (h3 - h2)… - (hk - h(k - 1)) ,将公式化简可以得到美丽值 = w1 +… wk + h1 - hk。那么设dp[i][j]为从前i个人里面选j个人排成一列:

(1)dp[i][j]可以分为前i个人里面选j个人,并且选第i个人;
(2)前i个人里面选j个人,并且不选第i个人;
将两种选择取max即可, 状态转移方程如下:
1.若j == 1 dp[i][j] = w[i] + h[i]
2.若j == k dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] + w[i] - h[i])
3.若j != 1 && j != k dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] + w[i]);

代码

#include"bits/stdc++.h"
 
using namespace std;
 
const int N = 1e5 + 10;
typedef pair<int, int> PII;
 
PII a[N];
long long dp[N][205];
 
bool cmp(PII x,PII y)
{
    return x.second < y.second;
}
 
int main()
{
    int n, k;
    cin >> n >> k;
    for(int i = 1; i <= n ;i ++)
    scanf("%d %d", &a[i].first,&a[i].second);
    
    for(int i = 0; i < N ;i ++)
    {
        for(int j = 1; j <= 200; j ++)
        dp[i][j] = -1e17;
    }
    sort(a + 1, a + 1 + n, cmp);
    
    for(int i = 1; i <= n ;i ++)
    {
        for(int j = 1; j <= min(i, k) ;j ++)
        {
            if(j == 1)
            dp[i][j] = max(dp[i - 1][j], (long long)a[i].first + a[i].second);
            else 
            {
                if(j != k)
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] + a[i].first);
                else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] + a[i].first - a[i].second);
            }
        }
    }
   cout << dp[n][k];
   
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值