10月集训test8

今天真是。。。感觉还不错呐,逃离月考的第一天,开心。
考试100+40+一顿乱搞不知所踪。。。
第一题本以为会卡掉最后一个点,没想到直接A了,看来打得比较精良。第二题的确打的是暴力,至于第三题。。。。根本什么都没想到。。
例行感谢 WKL 凯爷的倾情奉献,鞠躬撒花~

1.购买板凳

国庆节到了,豆豆家要来很多个客人,每个客人都要有一个板凳坐。现在豆豆得到了N条信息,每条信息如下——有A个人会在B时间到达,在C时间离开。(B时刻会占用板凳,C时刻不再占用板凳)。
豆豆想知道他最少需要准备多少个板凳使得每个客人都有板凳坐。

输入格式

第一行一个整数N代表信息条数。
接下来N行,每行格式如下“x a:b c:d”,表示X个人在a时b分到达,c时d分离开。

输出格式

输出一个整数表示最少需要的板凳数目。

输入样例1

2
6 08:00 09:00
5 08:59 09:59

输出样例1

11

输入样例2

2
6 08:00 09:00
5 09:00 10:00

输出样例2

6

数据范围

对于95%的数据:n≤10000
对于100%的数据:1≤n≤100000;1≤x≤100;0≤a,c<24;0≤b,d<60。

由于几小时几分直接计算会很麻烦,所以先将其换算成分钟,然后在
x1分到x2分的时候,开一个时间数组,这段时间人数加x,最后遍历整个数组输出最大值即可。
据说用差分可以降低复杂度跑得更快。。。dalao们可以自己试试。。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;

int n,x,x1,x2;
long long ans[2000],anss;
char a[7],b[7];

inline int read()
{
    int i=0;
    char c;
    for(c=getchar();c<'0'||c>'9';c=getchar());
    for(;c>='0'&&c<='9';c=getchar()) 
        i=(i<<3)+(i<<1)+c-'0';
    return i;
}

int main()
{
    //freopen("chair.in","r",stdin);
    //freopen("chair.out","w",stdout);

    n=read();
    while(n--)
    {
        x=read();
        cin>>a>>b;
        x1=((a[0]-'0')*10+(a[1]-'0'))*60+(a[3]-'0')*10+(a[4]-'0');     //换算。
        x2=((b[0]-'0')*10+(b[1]-'0'))*60+(b[3]-'0')*10+(b[4]-'0');     //换算。
        for(int i=x1;i<x2;i++)
            ans[i]+=x;
    }
    for(int i=1;i<=1500;i++)
        anss=max(anss,ans[i]);
    cout<<anss<<endl;
    return 0;
}

2.新排序

问题描述

世界上有很多种排序方式,比如插入排序,快速排序,随机排序,指鹿为马排序,猴子排序。
豆豆发明了一种新的排序方式叫做豆豆排序。算法流程如下:
首先找到所有不和谐的数字,一个不和谐的数字定义如下:
这个数字严格小于它左边的一个数字(如果左边有数字的话)或者严格大于右边的一个数字(如果右边有数字的话)
如果没有不和谐的数字,算法结束。
否则删除掉所有不和谐的数字,然后重复第一步。
现在豆豆想知道最终排序好的数列会是怎样的。

输入格式

第一行一个整数T代表数据组数。
接下来T组,每组第一行为一个整数N表示数列初始长度。
每组数据第二行N个数字表示输入的序列,数字<=10^9

输出格式

对于每组数据输出两行,第一行一个整数M表示剩下数列的长度,第二行M个数表示剩下的数列。

输入样例

1
5
1 2 3 4 5
5
5 4 3 2 1
5
1 2 3 2 1
5
2 4 1 3 5

输出样例

5
1 2 3 4 5
0

2
1 2
3
2 3 5

数据范围

对于40%的数据,N<=1000
对于70%的数据,N<=50000
对于100%的数据,T<=5;N<=100000

唔,虽然最终思想都差不多,但是别人过了的代码显然更加高明,这里用的是ZJJ的代码以及思想。
首先将原数列变成许多个单调递增的小数列(顺便减去单调递减的部分),然后减去各个数列的头和尾,若减去头尾后两个数列能接在一起,就合并成一个数列,继续计算,直到只剩下一个数列为止。
这样比直接暴力模拟题意要简洁快速得多。。。。
来一份无水版代码。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;

const int N=100010,inf=0x7fffffff;
struct node
{
    int vv,ps;
}b[N];
int t,n,tot,tmp,bz;
int a[N],in[N];
bool f[N];

inline int read()
{
    int i=0;
    char c;
    for(c=getchar();c<'0'||c>'9';c=getchar());
    for(;c>='0'&&c<='9';c=getchar())
        i=(i<<1)+(i<<3)+c-'0';
    return i;
}

int main()
{
    //freopen("sort.in","r",stdin);
    //freopen("sort.out","w",stdout);

    t=read();
    while(t--)
    {
        memset(f,0,sizeof(f));
        memset(in,0,sizeof(in));

        n=read();
        a[0]=-inf,a[n+1]=inf;
        for(int i=1;i<=n;i++)
            a[i]=read();
        for(int i=1;i<=n;i++)
            if(a[i-1]>a[i]||a[i]>a[i+1])
                f[i]=1;
        tot=0;
        for(int i=1;i<=n;i++)
            if(f[i])
            {
                if(i>1&&!f[i-1]&&!in[i-1])
                    b[++tot]=(node){a[i-1],i-1},in[i-1]=1;
                if(i<n&&!f[i+1]&&!in[i+1])
                    b[++tot]=(node){a[i+1],i+1},in[i+1]=1;
            }
        bz=1;
        while(bz)
        {
            bz=0;
            b[0].vv=-inf,b[tot+1].vv=inf;
            for(int i=1;i<=tot;i++)
            {
                in[b[i].ps]=0;
                if(b[i-1].vv>b[i].vv||b[i].vv>b[i+1].vv)
                    bz=1,f[b[i].ps]=1;
            }
            tmp=0;
            for(int i=1;i<=tot;i++)
            {
                if(f[b[i].ps])
                {
                    int p=b[i].ps;
                    if(p>1&&!f[p-1]&&!in[p-1])
                        b[++tmp]=(node){a[p-1],p-1},in[p-1]=1;
                    if(p<n&&!f[p+1]&&!in[p+1])
                        b[++tmp]=(node){a[p+1],p+1},in[p+1]=1;
                }
                else
                    b[++tmp]=b[i];
            }
            tot=tmp;    
        }
        tot=0;
        for(int i=1;i<=n;i++)
            if(!f[i])
                tot++;
        cout<<tot<<endl;
        for(int i=1;i<=n;i++)
            if(!f[i])
                cout<<a[i]<<" ";
        cout<<endl;
    }
    return 0;
}

哦还有考场上写的40分暴力模拟(一阵乱搞还是逃不出暴力的命):

//暴力40% 
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;

int t,n,aa,bb,tot;
long long a[100010],b[100010];
bool f[100010];

inline int read()
{
    int i=0;
    char c;
    for(c=getchar();c<'0'||c>'9';c=getchar());
    for(;c>='0'&&c<='9';c=getchar())
        i=(i<<1)+(i<<3)+c-'0';
    return i;
}

inline int czh(int n,int i)
{
    int ans=0;
    memset(f,false,sizeof(f));
    if(i==1)
    {
        for(int i=2;i<n;i++)
            if(a[i]<a[i-1]||a[i]>a[i+1])
                f[i]=true,ans++;
        if(a[1]>a[2]&&n>1)
            f[1]=true,ans++;
        if(a[n]<a[n-1]&&n>1)
            f[n]=true,ans++;
    }
    else
    {
        for(int i=2;i<n;i++)
            if(b[i]<b[i-1]||b[i]>b[i+1])
                f[i]=true,ans++;
        if(b[1]>b[2]&&n>1)
            f[1]=true,ans++;
        if(b[n]<b[n-1]&&n>1)
            f[n]=true,ans++;
    }
    return ans;
}

int main()
{
    //freopen("sort.in","r",stdin);
    //freopen("sort.out","w",stdout);

    t=read();
    while(t--)
    {
        n=read();
        for(int i=1;i<=n;i++)
            a[i]=read();
        aa=n,bb=1;
        while(czh(aa,bb))
        {
            tot=0;
            if(bb==1)
            {
                for(int i=1;i<=aa;i++)
                {
                    if(f[i]==true)
                        continue;
                    else
                        b[++tot]=a[i];
                }
                bb=2,aa=tot;
            }
            else
            {
                for(int i=1;i<=aa;i++)
                {
                    if(f[i]==true)
                        continue;
                    else
                        a[++tot]=b[i];
                }
                bb=1,aa=tot;
            }
        }
        if(bb==1)
        {
            cout<<aa<<endl;
            for(int i=1;i<=aa;i++)
                cout<<a[i]<<" ";
            cout<<endl;
        }
        else
        {
            cout<<aa<<endl;
            for(int i=1;i<=aa;i++)
                cout<<b[i]<<" ";
            cout<<endl;
        }
    }
    return 0;
}

3.豆豆游戏

吃饭,睡觉,打豆豆。
企鹅们的日常游戏打豆豆规则如下——这个游戏非常类似于人类的祖玛,也就是三个以上连在一起的相同颜色的豆豆可以消除。但是在只有两种颜色的企鹅中,这个游戏也只有两个豆豆,黑色豆豆和白色豆豆。
每次操作你可以向任意一个位置插入一个白色或者黑色的豆豆,然后消除三个及以上连在一起的豆豆,注意可能发生连锁反应。
现在企鹅豆豆想赢得这个游戏,所以它来找你帮它计算消除所以豆豆最少需要多少个操作?

输入格式

第一行一个整数T代表数据组数。
接下来T行,每行一个长度为N由”0”、”1”组成的字符串,表示初始豆豆状态(保证没有三个以上连在一起的同色的豆豆)

输出格式

输出T行表示需要的最小操作数。

输入样例

4
11
10101
101001001
01001101011001100

输出样例

1
4
3
2

数据范围

对于30%的数据:n≤9;
对于另外40%的数据:T=1,且数据随机;
对于100%的数据:T≤10;n≤200。

考试的时候对这道题完全没有思路,一阵乱搞还是没有捞到几个得分(多组数据想水过是不可能的。。)
所以直接来说说正解的做法。
区间dp,由于连在一起的同色的两个豆豆一定是一起被消除的,所以首先将他们合并成一个点,并记录点权。
dp[l][r]表示[l,r]的豆豆全部被消除的最小次数,num[i]表示i点的点权,状态转移方成分为四种:
1> 3-num[l] (l=r)
此时只有一种颜色的豆豆,则加到三个将其消除。
2> dp[l+1][r-1]+max(0,3-num[l]-num[r]) (此时l,r所代表的豆豆的颜色相等)
这种情况例如”1001”,在中间加一个0然后左右碰在一起,即先将中间部分消完,再加上两侧并在一起要消完需要加的豆豆数。若是两侧的加起来直接大于3个,则不需要再加。
3> dp[l][k]+dp[k+1][r] (l≤k<r)
将整个区间从k点分成两个部分,分别计算。
【然而有时候这样计算出来的并不对,万一从中间分开的时候,恰好分开了连在一起的一对,很容易造成一边在消最后一次的时候顺带着把另一边的一部分也消掉了,这种分析见4】
4> dp[l+1][k-1]+dp[k+1][r-1] (l,k,r点的豆豆颜色相等且num[l]+num[r]<4且num[k]=1)
(找出中间的特殊1,最后左边还剩1个1,右边也是,加起来刚好一起消掉。)
详情可见一个经典样例 1100110010110100110011
答案为3,不是2。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;

int t,n,cnt;
int dp[210][210],num[210],ch[210];
char s[210];

inline int read()
{
    int i=0;
    char c;
    for(c=getchar();c<'0'||c>'9';c=getchar());
    for(;c>='0'&&c<='9';c=getchar())
        i=(i<<1)+(i<<3)+c-'0';
    return i;
}

int main()
{
    //freopen("beans.in","r",stdin);
    //freopen("beans.out","w",stdout);

    t=read();
    while(t--)
    {
        memset(dp,0x3f,sizeof(dp));
        memset(num,0,sizeof(num));

        scanf("%s",s+1);
        n=strlen(s+1),cnt=0;
        for(int i=1;i<=n;i++)
        {
            if(s[i]!=s[i-1])
                ch[++cnt]=s[i],num[cnt]=1;
            else
                num[cnt]++;
        }   
        for(int i=cnt;i>=1;i--)
            for(int j=i;j<=cnt;j++)
            {
                if(i==j)
                {
                    dp[i][j]=3-num[i];
                    continue;
                }
                if(ch[i]==ch[j])
                {
                    dp[i][j]=dp[i+1][j-1]+max(0,3-num[i]-num[j]);
                    if(num[i]+num[j]<4)
                        for(int k=i+2;k<j;k+=2)
                            if(num[k]==1)
                                dp[i][j]=min(dp[i][j],dp[i+1][k-1]+dp[k+1][j-1]);
                }
                for(int k=i;k<j;k++)
                    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
            }
        cout<<dp[1][cnt]<<endl;
    }
    return 0;
}

乍暖还寒时候,最难将息。
以上。
来自2017.10.17.

——我认为return 0,是一个时代的终结。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值