暑假每日一题(三)


一、亮灯时长(后缀和)

题目链接
自习室内有一个智能灯。

在 0 时刻,管理员会将打开电闸,并将灯点亮。

在 M 时刻,管理员会直接拉下电闸,此时,如果灯处于点亮状态,则会因为断电而熄灭。

在 0∼M 之间有 n 个不同时刻,不妨用 a1,a2,…,an 表示,其中 0<a1<a2<…<an<M。

在这 n 个时刻中的每个时刻,管理员都会拨动一次智能灯的开关,使灯的状态切换(亮变灭、灭变亮)。

现在,你可以最多额外指定一个时刻(也可以不指定),让管理员在此时刻也拨动开关一次。注意选定的时刻不能与 a1,a2,…,an 相等。

你的目的是让亮灯的总时长尽可能长。

输出这个最大亮灯总时长。

输入格式
第一行包含整数 T,表示共有 T 组测试数据。

每组数据,第一行包含两个整数 n 和 M。

第二行包含 n 个整数 a1,a2,…,an。

输出格式
输出一个整数,表示最大亮灯总时长。

数据范围
1≤T≤30,
1≤n≤105,
2≤M≤109,
0<a1<a2<…<an<M。
同一测试点内所有 n 的和不超过 105。

输入样例:
3
3 10
4 6 7
2 12
1 10
2 7
3 4
输出样例:
8
9
6

假定我们按的是ai:
奇数区间:ai之前的奇数加上ai+1之后的偶数区间和ai+1-ai-1这个长度,是利用前缀和技巧
偶数区间:ai前的奇数区间和ai+1后的偶数区间和ai+1-ai
所以不分奇偶

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<string.h>
using namespace std;

const int N=100010;
int n,m;
int a[N],s1[N],s2[N];

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n,m;
        cin>>n>>m;
        memset(a,0,sizeof(a));
        memset(s1,0,sizeof(s1));
        memset(s2,0,sizeof(s2));
        for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
        a[++n]=m;
        s1[N]=s2[N]=0;
        for(int i=n-1;i>=0;i--)
        {
            s1[i]=s1[i+1],s2[i]=s2[i+1];
            if(i%2==0)s1[i]+=a[i+1]-a[i];
            else s2[i]+=a[i+1]-a[i];
        }

        int res=s1[0];
        for(int i=0;i<n;i++)
        {
            int t=a[i+1]-a[i];
            if(t==1)continue;
            res=max(res,s1[0]-s1[i]+s2[i+1]+t-1);
        }
        printf("%d\n",res);
    }
    return 0;
}

二、兔子跳

题目链接
一只兔子位于二维平面的原点 (0,0) 处,它想通过一系列的跳跃,跳到点 (x,0) 处。

给定一个长度为 n 的数组 a1,a2,…,an。

兔子能从一个点跳到另一个点,当且仅当两点之间的距离等于上述数组中的某个元素的值。

请问,兔子从 (0,0) 到 (x,0) 最少需要跳几次?

注意,兔子可以跳到非整数坐标的点上。

例如,当 x=4,a={1,3} 时,(0,0)→(1,0)→(4,0) 和 (0,0)→(2,5√)→(4,0) 均为合理最佳方案之一。

输入格式
第一行包含整数 T,表示共有 T 组测试数据。

每组数据第一行包含两个整数 n 和 x。

第二行包含 n 个整数 a1,a2,…,an。

输出格式
每组数据输出一行结果,表示最少跳跃次数。

数据范围
1≤T≤1000,
1≤n≤105,
1≤x≤109,
1≤ai≤109,ai 各不相同。
保证同一测试点内所有 n 的和不超过 105。

输入样例:
4
2 4
1 3
3 12
3 4 5
1 5
5
2 10
15 4
输出样例:
2
3
1
2

当x等于a,那么一次可以跳到
当x小于a,那么一定存在一个等腰三角形,使之两次跳到
当x大于a,那么x/a为整数的时候,那就是那个值,如果不为整数,那么除以之后下取整,如果剩下的长度<2a,那么就可以再构造一次等腰三角形了,综上,此时的情况就是a/x或者a/x下取整+1,那么可以统一表示为a/x上取整

#include<iostream>
#include<algorithm>

using namespace std;

const int N=100010;
int a[N];

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n,x;
        cin>>n>>x;
        bool flag=false;
        int a=0;
        while(n--)
        {
            int t;
            scanf("%d",&t);
            if(t==x)flag=true;
            a=max(a,t);
        }
     if(flag)puts("1");
     else if(x<a)puts("2");
     else printf("%d\n",(x+a-1)/a);//上取整   
    }
    return 0;
}

三、数组补全(环图,要多回顾)

题目链接
给定一个 1∼n 的排列 f1,f2,…,fn。

已知,对于 1≤i≤n,fi≠i 始终成立。

现在,因为一些原因,数组中的部分元素丢失了。

请你将数组丢失的部分补全,要求数组在补全后仍然是一个 1∼n 的排列,并且对于 1≤i≤n, fi≠i 均成立。

输入格式
第一行包含整数 T,表示共有 T 组测试数据。

每组数据第一行包含一个整数 n。

第二行包含 n 个整数 f1,f2,…,fn。如果 fi=0,则表示 fi 已经丢失,需要补全。

输出格式
每组数据一行,输出补全后的 f 数组,整数之间空格隔开。

如果方案不唯一,则输出任意合理方案即可。

数据范围
1≤T≤100,
2≤n≤2×105,
0≤fi≤n,至少两个 fi 为 0。
同一测试点内所有 n 的和不超过 2×105。
数据保证有解。

输入样例:
3
5
5 0 0 2 4
7
7 0 0 1 4 0 6
7
7 4 0 3 0 5 1
输出样例:
5 3 1 2 4
7 3 2 1 4 5 6
7 4 2 3 6 5 1

i和fi构成环图,每个点的入度和出度均为1,把没有出现过的数加入缺口环的后面,剩下就直接封口,如果所有的环没有缺口,那么就把没出现过的数字自己连成一个环即可
在这里插入图片描述

#include<iostream>
#include<algorithm>
#include<stack>
#include<string.h>

using namespace std;
const int N=200010;

int n;
int p[N],q[N];
bool st[N];

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        memset(q,0,sizeof(q));
        memset(st,0,sizeof(st));
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&p[i]);
            q[p[i]]]=i;
        }

        bool flag=false;
        for(int i=1; i<=n; i++)
        {
            if(st[i]||!p[i])//没有处理过或者p[i]为0
                continue;
            st[i]=true;
            int x=i,y=i;
            while(p[x]&&!st[p[x]])
            {
                x=p[x];
                st[x]=true;
            }
            while(q[y]&&!st[q[y]])
            {
                y=q[y];
                st[y]=true;
            }
            if(p[x]==y)continue;//孤立点
            if(!flag)
            {
                flag=true;
                for(int j=1; j<=n; j++)
                    if(!p[j]&&!q[j])
                    {
                        st[j]=true;
                        p[x]=j;
                        x=j;
                    }
            }
            p[x]=y;
        }
        if(!flag)
        {
            int x=0,y=0;
            for(int i=1;i<=n;i++)
                if(!p[i])
            {
                if(!x&&!y)x=y=i;
                else
                {
                    p[x]=i;
                    x=i;
                }
            }
            p[x]=y;
        }
        for(int i=1;i<=n;i++)
            printf("%d",p[i]);
        puts("");
    }
    return 0;
}

四、水果拼盘

题目链接

某水果店以出售水果拼盘盈利。

该店铺共提供两种拼盘:

拼盘 1:包含 1 个苹果,1 个香蕉。
拼盘 2:包含 1 个梨,1 个橙子,1 个香蕉。
已知该店铺共有 a 个苹果,b 个梨,c 个橙子,d 个香蕉。

拼盘 1 的利润为 e 元,拼盘 2 的利润为 f 元。

水果店的生意很好,所有拼好的拼盘都会销售一空。

请问,利用现有水果拼装水果拼盘,能够获得的最大利润是多少?

注意,也许会有一些水果用不完,但这并不重要。

输入格式
第一行包含整数 T,表示共有 T 组测试数据。

每组数据占一行,包含 6 个整数,依次为 a,b,c,d,e,f。

输出格式
每组数据输出一行结果,表示最大利润。

数据范围
1≤T≤10。
1≤a,b,c,d≤105,
1≤e,f≤103。

输入样例:
3
4 5 6 3 1 2
12 11 13 20 4 6
17 14 5 21 15 17
输出样例:
6
102
325

会发现两个套餐,无论怎么样都会有一个香蕉拿出去,所以就看香蕉的数量来判断总共会有多少种情况
当e>=f的时候,尽可能的分给第一个套餐,那就取min(a,d),y=min(b,c,d-x)
当e<f的时候,尽可能分给第二个套餐,y=min(b,c,d),x=min(a,d-x)

#include<iostream>
#include<algorithm>
#include<stack>
#include<string.h>

using namespace std;


int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int a,b,c,d,e,f;
        cin>>a>>b>>c>>d>>e>>f;
        if(e>=f)
        {
            int x,y;
            x=min(a,d);
            y=min(b,min(c,d-x));
            cout<<x*e+y*f<<endl;
        }
        else {
            int x,y;
            y=min(b,min(c,d));
            x=min(a,d-y);
            cout<<x*e+y*f<<endl;
        }
    }
    return 0;
}

五、砖块(递推)

题目链接
n 个砖块排成一排,从左到右编号依次为 1∼n。

每个砖块要么是黑色的,要么是白色的。

现在你可以进行以下操作若干次(可以是 0 次):

选择两个相邻的砖块,反转它们的颜色。(黑变白,白变黑)

你的目标是通过不超过 3n 次操作,将所有砖块的颜色变得一致。

输入格式
第一行包含整数 T,表示共有 T 组测试数据。

每组数据第一行包含一个整数 n。

第二行包含一个长度为 n 的字符串 s。其中的每个字符都是 W 或 B,如果第 i 个字符是 W,则表示第 i 号砖块是白色的,如果第 i 个字符是 B,则表示第 i 个砖块是黑色的。

输出格式
每组数据,如果无解则输出一行 −1。

否则,首先输出一行 k,表示需要的操作次数。

如果 k>0,则还需再输出一行 k 个整数,p1,p2,…,pk。其中 pi 表示第 i 次操作,选中的砖块为 pi 和 pi+1 号砖块。

如果方案不唯一,则输出任意合理方案即可。

数据范围
1≤T≤10,
2≤n≤200。

输入样例:
4
8
BWWWWWWB
4
BWBB
5
WWWWW
3
BWB
输出样例:
3
6 2 4
-1
0
2
2 1

跟翻硬币和开关灯(费解的开关)很像,一个一维,一个二维。
本质考察的是递推,每一个位置操作2次就等于没有操纵,所有每个位置就考虑是操作一次还是操作两次
这道题也可以用高斯消元法来求解,就是会难一些O(n^3)
y1=x1^x1,就是当数据范围很大的时候用高斯消元法来写

#include<iostream>
#include<algorithm>
#include<vector>
#include<string.h>

using namespace std;

int n;
void updata(char &c)
{
    if(c=='W')c='B';
    else c='W';
}
bool check(string s,char c)
{
    vector<int>res;
    for(int i=0;i+1<n;i++)
    {
        if(s[i]!=c)
        {
            updata(s[i]);
            updata(s[i+1]);
            res.push_back(i);
        }
    }
    if(s.back()!=s[0])return false;
    cout<<res.size()<<endl;
    for(auto x:res)cout<<x+1<<" ";
    if(res.size())cout<<endl;
    return true;
}

int main()
{
  int T;
  cin>>T;
  while(T--)
  {
      string s;
      cin>>n>>s;
        if(!check(s,'W')&&!check(s,'B'))
            puts("-1");
  }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值