zoukankan      html  css  js  c++  java
  • codeforces --- Round #244 (Div. 2) B. Prison Transfer

    B. Prison Transfer

    The prison of your city has n prisoners. As the prison can't accommodate all of them, the city mayor has decided to transfer c of the prisoners to a prison located in another city.

    For this reason, he made the n prisoners to stand in a line, with a number written on their chests. The number is the severity of the crime he/she has committed. The greater the number, the more severe his/her crime was.

    Then, the mayor told you to choose the c prisoners, who will be transferred to the other prison. He also imposed two conditions. They are,

    • The chosen c prisoners has to form a contiguous segment of prisoners.
    • Any of the chosen prisoner's crime level should not be greater then t. Because, that will make the prisoner a severe criminal and the mayor doesn't want to take the risk of his running away during the transfer.

    Find the number of ways you can choose the c prisoners.

    Input

    The first line of input will contain three space separated integers n (1 ≤ n ≤ 2·105), t (0 ≤ t ≤ 109) and c (1 ≤ c ≤ n). The next line will contain n space separated integers, the ith integer is the severity ith prisoner's crime. The value of crime severities will be non-negative and will not exceed 109.

    Output

    Print a single integer — the number of ways you can choose the c prisoners.

    Sample test(s)
    Input
    4 3 3
    2 3 1 1
    Output
    2
    Input
    1 1 1
    2
    Output
    0
    Input
    11 4 2
    2 2 0 7 3 2 2 4 9 1 4
    Output
    6
    【题目链接】
    http://codeforces.com/contest/427/problem/B
    【题目大意】
    某市的监狱犯人关满了,需要从n个囚徒中选择c个转移至另外一个监狱,监狱长给出了选择的两个要求,让你计算有多少种方法然后输出方法数。
    要求:
    1.所选的囚徒必须都是连续的;
    2.所选的囚徒中,每个囚徒的犯罪严重程度不超过t。
    【题目分析】
    首先看懂题后,心中大喜,这么简单的题,二话不说就写代码,当时都没多考虑,写完测试数据都没测就提交了,OK,初判过了,然后就做下一题,最后可想而知,超时了。
    还是太粗心了,时间复杂度都没分析。附上比赛时提交的代码:
    比赛代码(超时):
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int main()
    {
        int n,t,c;
        int i,j,k;
        int flag;
        int cnt=0;
        scanf("%d%d%d",&n,&t,&c);
        int a[200010];
        for(i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(i=c;i<=n;i++)
        {
            flag=0;
            for(j=0;j<c;j++)
            {
                if(a[i-j]>t)
                {
                    flag=1;
                    break;
                }
            }
            if(flag==0)
                cnt++;
        }
        printf("%d
    ",cnt);
        return 0;
    }

    好吧,比赛结束了,rating也掉了,心也上得差不多了,那就再来重新总结一下,再做一遍。

    既然是超时,那就优化呗。

    一开始我想到了上面的代码每次比较的时候基本上都要c次的回溯,时间浪费的太多了,这就好比字符串的模式匹配的BF算法一样,那何不将BF算法像KMP一样优化一下呢(扯远了),不需要每次都回溯,每次判断的时候如果遇到了一个大于t的数,那么接下来的c个都不能和他去新监狱里愉快的玩耍了,所以接下来的这c个根本就没有回溯的必要,所以又有了一下的SB代码:

    代码(仍然超时):

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int main()
    {
        int n,t,c;
        int i,j,k;
        int flag;
        int cnt=0;
        scanf("%d%d%d",&n,&t,&c);
        int a[200010];
        for(i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(i=c;i<=n;)
        {
    //        k=i;
            flag=0;
            for(j=0;j<c;j++)
            {
                if(a[i-j]>t)
                {
    
                    flag=1;
                    i+=c;
                    break;
                }
            }
            if(flag==0)
                {cnt++;i++;
    //            cout<<k<<"  "<<a[k]<<endl;
                }
        }
        printf("%d
    ",cnt);
        return 0;
    }

    提交后一看,还超时,好吧,继续优化,我又想到其实如果判断了k个后,如果这k个都是小于t的(以下简称合法的),那么我就不用回溯了,直接判断当前这个数是否合法,合法那么方法就加1,遇到不和法的数就再次开始直到连续合法数达到c,那就可以继续以上的统计了,我就用一个变量full来统计连续合法数的个数。

    代码(超时):

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int main()
    {
        int n,t,c;
        int i,j,k;
        int flag;
        int cnt=0;
        int full=0;
        scanf("%d%d%d",&n,&t,&c);
        int a[200010];
        for(i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(i=c;i<=n;)
        {
    //        k=i;
            flag=0;
        if(full>=c)
            {
                if(a[i]<=t)
                {
                    cnt++;
                    i++;
                    continue;
                }
                else
                {
                    flag=1;
                    i+=c;
                    full=0;
                    continue;
                }
            }
        else
        {
            for(j=0;j<c;j++)
            {
                if(a[i-j]>t)
                {
    
                    flag=1;
                    i+=c;
                    full=0;
                    break;
                }
                else
                    full++;
            }
        }
            if(flag==0)
                {cnt++;i++;
    //            cout<<k<<"  "<<a[k]<<endl;
                }
        }
        printf("%d
    ",cnt);
        return 0;
    }

    提交一看,What?还超时,我不得不怀疑我的方法的正确性了,在吃饭的路上我独自想了一下,突然发现这就是一个简单的区间统计吗?只用扫描一遍统计出每一段数值都不超过t的区间有多少个不就可以了吗。

    AC代码:

    #include<iostream>
    using namespace std;
    int main()
    {
        int n,t,c;
        int i,j,k;
        int ans=0;
        int cnt=0;
        cin>>n>>t>>c;
        for(i=0;i<n;++i)
        {
            cin>>k;
            if(k>t)
            {
                if(cnt>=c)
                    ans+=cnt-c+1;
                cnt=0;
            }
            else
                cnt++;
        }
        if(cnt>=c)    //对最后一个cnt进行判断
            ans+=cnt-c+1;
        cout<<ans<<endl;
        return 0;
    }

    经过了一道水题的这般周折以后,我还是悟到了一些东西,codeforces div.2中的比赛一般是考理解英语题目的能力和解决一些小问题的能力,算法一般考的很少,但是这些方法都很巧妙,所以还是能锻炼编程能力的。

  • 相关阅读:
    SQLite存储类(数据类型)
    SQLite常用命令
    C# 跨线程操作无效
    Android打开新的Activity并同时关闭当前Activity
    SQLite实现Top功能
    Android调用Sqlite数据库时自动生成db-journal文件的原因
    C#使用SqlDataReader读取数据库数据时CommandBehavior.CloseConnection参数的作用
    Android计算时间差
    PS通道抠图总结
    Android再次激活Activity时触发事件用于列表重新读取载入
  • 原文地址:https://www.cnblogs.com/crazyacking/p/3707989.html
Copyright © 2011-2022 走看看