[Codeforces297D]Color the Carpet(构造)

=== ===

这里放传送门

=== ===

题解

这题我记得是我放了假去理发排队的时候正好爸爸车上有我的笔记本电脑然后就拿出来把这题写了结果都轮到我理发了好像还没写完2333

这道题的意思是给出一个网格和可用的颜色数量,然后给出每相邻两个格子的约束条件,要求构造一组方案,使得没有被满足的约束条件不超过所有条件的1/4。

考虑如何才能让不被满足的条件不超过1/4。假设网格是n*m的,那么如果不考虑竖着的那n个限制,横着的m个限制是一定能够被全部满足的。要满足“横着的”限制,显然就是一列一列的填格子,因为“横着的”限制只跟上下两个格子有关。并且可以只用两种颜色来回填。那么当不考虑“竖着的”限制的时候,“横着的”限制就可以被全部满足,那么如果让n和m里面较大的那一个当做“横着的”限制,那么满足这些以后就已经满足了所有限制的1/2。然后再考虑满足“竖着的”限制,仍然是一列一列的看。如果能保证每一列上没有被满足的“竖着的”限制不超过这一列的1/2,那么因为本身“竖着的”限制就比“横着的”限制少,就算所有“竖着的”限制只满足了一半,加上那些已经被满足了的“横着的”限制也最少就是3/4。那么当扫描到某一列的时候发现它跟它旁边那一列的冲突数量超过了总数的1/2,就把这一列的格子颜色全部取反。取反以后横着的限制仍然是满足的,但竖着的限制状态都取反了,那么没有被满足的数量就可以被控制在1/2以内,就可以满足要求了。

这道题比较麻烦的地方在于预处理啊。。因为它读入本来就很麻烦,并且还要保证“横着的”限制多于“竖着的”限制,否则的话还要把网格转一下。。并且还有一个问题就是因为上面说的所有的前提都是可用颜色数量大于等于2,因为满足“横着的”限制的时候是用两种颜色倒着填的。不过只能用一种颜色的时候也好办,统计一下不等号的数量就可以了。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int h,w,k,r1,r2,c1,c2,clr[1010][1010],N,tot,unf;
bool r[1010][1010],c[1010][1010];
char get(){
    char ch=getchar();
    while (ch!='N'&&ch!='E') ch=getchar();
    return ch;
}
void Swaping(){
    bool tmp[1010][1010];
    memcpy(tmp,r,sizeof(r));
    memset(r,false,sizeof(r));
    for (int i=1;i<=c1;i++)
      for (int j=1;j<=c2;j++)
        r[j][i]=c[i][j];一列列读c数组放到r里
    memset(c,false,sizeof(c));
    for (int i=1;i<=r1;i++)
      for (int j=1;j<=r2;j++)
        c[j][i]=tmp[i][j];
    swap(h,w);N=1;//打上交换标记
    r1=h-1;r2=w;c1=h;c2=w-1;//注意记录数组规模的变量也要随着交换
    return; 
}
int main()
{
    scanf("%d%d%d",&h,&w,&k);
    for (int i=1;i<=2*h-1;i++){
        int x=(i&1)?w-1:w;
        if (x==w-1) ++c1;
        else ++r1;
        for (int j=1;j<=x;j++){
            char ch=get();
            if (ch=='N') ++N;//为了特判k=1的情况而记录N的个数
            ++tot;
            if (x==w-1) c[c1][j]=(ch=='E');
            else r[r1][j]=(ch=='E');  
        }
    }
    r2=w;c2=w-1;
    if (k==1){
        if (N<=tot/4){
            printf("YES\n");
            for (int i=1;i<=h;i++)
              for (int j=1;j<=w;j++)
                printf("1%c"," \n"[j==w]);
        }else printf("NO\n");
        return 0;
    }
    N=0;
    if (r1*r2<c1*c2) Swaping();//如果列上的要求比行上的要求多就交换
    memset(clr,-1,sizeof(clr));
    for (int i=1;i<=r2;i++) clr[1][i]=0;//每一行都是和它上面的一行进行匹配
     for (int j=1;j<=r2;j++)//所以第一行可以全部填0
      for (int i=2;i<=r1+1;i++)
        if (r[i-1][j]==false) clr[i][j]=clr[i-1][j]^1;
        else clr[i][j]=clr[i-1][j];
    for (int j=2;j<=c2+1;j++){
        int cnt=0;
        for (int i=1;i<=c1;i++)
          if (clr[i][j]==clr[i][j-1])//每一列和它左边的一列进行匹配
            cnt+=(c[i][j-1]==false);
          else cnt+=(c[i][j-1]==true);
        if (cnt>h/2)
          for (int i=1;i<=c1;i++)
            clr[i][j]=clr[i][j]^1;
    }
    printf("YES\n");
    if (N!=0)//如果交换过就改变输出顺序
      for (int i=1;i<=w;i++)
        for (int j=1;j<=h;j++)
          printf("%d%c",clr[j][i]+1," \n"[j==h]);
    else
      for (int i=1;i<=h;i++)
        for (int j=1;j<=w;j++)
          printf("%d%c",clr[i][j]+1," \n"[j==w]);
    printf("\n");
    return 0;
}

偏偏在最后出现的补充说明

构造题的思路方法和普通的题感觉好不一样啊。。还是要多考虑一下它的性质啊。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值