Codeforces Round #545 (Div. 2) A Sushi for Two,B Circus,C. Skyscrapers

7 篇文章 0 订阅
1 篇文章 0 订阅

这次div2好难。。打完看介绍才发现不对劲。。
坑了一段时间,发生了好多糟糕的事情 = =、

传送门:Codeforces Round #545 (Div. 2)


A题

题意
给n个数字,这些数字非1即2,要求找出一段连续的长度为len的区间,前len/2为1(或者2),后len/2为2(或者1),输出符合的len的最大值

思路
记录连续相同的数字的长度,比如 2 2 2 1 1 2 2,记录为 3 2 2,然后在两两匹配的最小值中找最大的,就是答案。

代码
注意的是,写的时候cnt[maxn]没有初始化为0,但还是pretest passed了,醒来后发现wa在test35 QAQ这谁顶得住啊.jpg(nmd,wsm!!!)

int main()
{   if(fopen("out1.txt","r")) freopen("out1.txt","r",stdin);
    int T,n,m,k,i,sum,j,t,tmp,flag = 0;
    scanf("%d",&n);
    int a[maxn],cnt[maxn]={0};
    sum = 0;
    /*
    for(i=0;i<n;i++){
        scanf("%d",&a[i]);
        if(i==0) { cnt[a[i]]++; continue; }
        if(a[i]!=a[i-1]){
            if(flag==0){
                flag = 1;
                cnt[a[i]]++;
            }else if(flag==1){
                cnt[a[i]]++;
                sum = max(sum,min(cnt[1],cnt[2]));
                cnt[1] = cnt[2] = flag = 0;
            }
        }else{
            cnt[a[i]]++;
        }
    }
    sum = max(sum,min(cnt[1],cnt[2]));
    */
    t = 0;
    cnt[t] = 1;
    for(i=0;i<n;i++){
        scanf("%d",&a[i]);
        if(i==0) continue;
        if(a[i]==a[i-1]){
            cnt[t]++;
        }else cnt[(++t)]++;
    }
    for(i=1;i<=t;i++){
        sum = max(sum,min(cnt[i-1],cnt[i]));
    }
    printf("%d\n",sum*2);
    return 0;
}

B题

题意
第一行一个n,表示有n个演员,有些演员可以扮演clown,有些可以扮演acrobat。第二行有n个0或1,表示第i个演员可不可以扮演clown,第三行有n个0或1,表示第i个演员可不可以扮演acrobat。要选择 n/2 个演员出演第一场,并输出所选的这 n/2 个演员的编号。
要求第一场出演的演员,能扮演clown的,和第二场出演的演员,能扮演acrobat的,数量一样。(只能扮演clown的演员,可以放在第二场出演,则演不了clown又演不了acrobat)

假思路
00表示都不能演,10表示只能演clown,01表示只能演acrobat,11表示都能演。
统计00,10,01,11的个数,一个 10 和 一个01 搭配,如果 10 比 01 多,则多出来的和11匹配,少的话,01 多出来的和11匹配,如果数量一样的话,如果11是奇数的话,就输出-1,否则分一半匹配。。。
然后数量一样的那里,第一个例子就验证是错的了。。。

官方思路
设 00 ,01 ,10 ,11 的总数分别是 n a n_a na n b n_b nb n c n_c nc n d n_d nd。设第一轮中, 00 ,01 ,10 ,11 这四种演员的数量分别是 a , b , c , d ,所以,第二轮中, 00 ,01 ,10 ,11 这四种演员的数量分别是 n a n_a na - a, n b n_b nb - b, n c n_c nc - c, n d n_d nd - d。根据题目有:a + b + c + d = n 2 \dfrac{n}{2} 2n 。而第一轮选择的演员能扮演clown的有 c + d 个,第二轮中选择的演员能扮演acrobat的有( n b n_b nb - b )+ ( n d n_d nd - d )个,根据题目要求:c + d = ( n b n_b nb - b )+ ( n d n_d nd - d ),整理得 c + b + 2d = n b n_b nb + n d n_d nd

现在知道了两个式子,和四个未知数,可以用暴力来解决:确定a,b,c,d中 其中两个,来求出剩下两个,在看看这四个是否符合数据范围:0 ≤ &ThickSpace; \leq\; x ≤ &ThickSpace; \leq\; nx

思路对比
emmm 设出四个未知数之后,不应该去考虑搭配的问题,因为有很多种搭配 = = 想到后来都爆炸了,还想着10和01的数量不能超过 n 2 \dfrac{n}{2} 2n,然后之后怎么怎么样。。。。没有想到去列出式子,然后直接暴力。第一个式子好列,第二个式子需要想想,但也不难知道。复杂度感觉是 n 2 n^2 n2,不过5000的 n 2 n^2 n2会超时啊。。。看了题解实际上是 5000 2 2 \frac{5000}{2}^2 250002,吧。

代码

int T,n,m,k,i,sum,j,td,ta,tmp;
int na,nb,nc,nd,a,b,c,d;
char sc[5005],sa[5005];

int main()
{   if(fopen("in.txt","r")) freopen("in.txt","r",stdin);
    scanf("%d%s%s",&n,sc,sa);
    for(i=0;i<n;i++){
        if(sc[i]=='0'&&sa[i]=='0') na++;
        else if(sc[i]=='0'&&sa[i]=='1') nb++;
        else if(sc[i]=='1'&&sa[i]=='0') nc++;
        else if(sc[i]=='1'&&sa[i]=='1') nd++;
    }
    int flag = 0;
    for(i=0;i<=nb;i++){
        for(j=0;j<=nc;j++){
            td = nb+nd-i-j;
            if(td<0||td&1) continue;
            td /= 2;
            if(td>nd) continue;
            ta = n/2 - i - j - td;
            if(0<=ta&&ta<=na){
                flag = 1;
                break;
            }
        }
        if(flag) break;
    }
    if(!flag) printf("-1\n");
    else{
        a = ta; b = i; c = j; d = td;
       // cout<<a<<" "<<b<<" "<<c<<" "<<d<<endl;
        for(i=0;i<n;i++){
            if(sc[i]=='0'&&sa[i]=='0'&&a>0){
                if(a--) printf("%d%c",i+1,i==n-1?'\n':' ');
            }else if(sc[i]=='0'&&sa[i]=='1'&&b>0){
                if(b--) printf("%d%c",i+1,i==n-1?'\n':' ');
            }else if(sc[i]=='1'&&sa[i]=='0'&&c>0){
                if(c--) printf("%d%c",i+1,i==n-1?'\n':' ');
            }else if(sc[i]=='1'&&sa[i]=='1'&&d>0){
                if(d--) printf("%d%c",i+1,i==n-1?'\n':' ');
            }
        }
    }
    return 0;
}

C题

题意
给大小为 n ∗ m n*m nm的矩阵,表示 n ∗ m n*m nm 个高楼的高度。对于第 i 行第 j 列组成的十字,要求给楼的高度编号,整个图从1开始编号(某行/列可以缺一些数字),更高的楼编号更大。第 i 行和第 j 列的楼的高度不互相影响,只用参照本行/列的高度进行编号。最后输出这个十字中的最大编号。

思路
首先离散化一下,用的map离散。然后得出一个数在那行和那列中排第几。取该数在它的行与列中排名较大的。如果是行中排列比较大的,那么就要在行的基础上,加上在列中排名的“偏移量”,比如行中排x名,列中排y名,就要计算出该数在列中头顶还排有多少个比它大的数字,计算出的差就是“偏移量”,x+偏移量 才可能对答案做贡献,最后把它和该行的最大编号中取最大值。

对于行和列中取了排名较小的 的话,例子:
2 2
1 2
3 4

对于3,3在行中编号为1,列中编号为2,如果取排名较小的话,那就是先看横行再看纵行,变成:
0
1 2
编号0这里不好计算,如果取编号大的,会比较好算:
1
2 3

好像讲得有点啰嗦了 = =

代码

int a[1005][1005],row[1005][1005],col[1005][1005];
int maxrow[1005],maxcol[1005];

int main()
{   if(fopen("in.txt","r")) freopen("in.txt","r",stdin);
    int T,n,m,k,i,sum,j,t,tmp;
    scanf("%d%d",&n,&m);
    for(i=0;i<n;i++)
        for(j=0;j<m;j++) scanf("%d",&a[i][j]);
    for(i=0;i<n;i++){
        map<int,int > mp; t = 0;
        for(j=0;j<m;j++) mp[a[i][j]];
        for(auto &it:mp) it.second = ++t;
        for(j=0;j<m;j++) row[i][j] = mp[a[i][j]];
        maxrow[i] = t;
       // for(j=0;j<m;j++) printf("%d -  ",row[i][j]);
    }
    for(i=0;i<m;i++){
        map<int,int >mp; t = 0;
        for(j=0;j<n;j++) mp[a[j][i]];
        for(auto &it:mp) it.second = ++t;
        for(j=0;j<n;j++) col[j][i] = mp[a[j][i]];
        maxcol[i] = t;
       // for(j=0;j<n;j++) printf("%d :   ",col[j][i]);
       // cout<<endl;
    }
    //cout<<maxrow[3]<<endl;
    for(i=0;i<n;i++){
        for(j=0;j<m;j++){
           // cout<<col[i][j]<<" - "<<row[i][j]<<" "<<maxrow[i]<<" "<<maxcol[i]<<endl;
            if(col[i][j]>row[i][j]){
                t = max(col[i][j] + maxrow[i] - row[i][j],maxcol[j]);
            }else{
                t = max(row[i][j] + maxcol[j] - col[i][j],maxrow[i]);
            }
            printf("%d%c",t,j==m-1?'\n':' ');
            //printf("%d\n",t);
        }
    }
    return 0;
}
/*
3 5
12 7 15 4 9
3 1 5 12 15
19 17 25 10 21
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值