NKOJ-1521 芯片制作(染色法构图)

17 篇文章 0 订阅

P1521芯片制作
时间限制 : 10000 MS 空间限制 : 65536 KB

问题描述
   计算机的大脑通常被称为CPU。
现在国家XX局交给小A一个重要任务,要求他制作一种新型的CPU,幸运的是,CPU研究中心已经将好多个CPU内核蚀刻在硅片上了,这种硅片很可爱,它是由3n^2个内核拼成的L型。当n=5的时候,硅片的形状如下图所示。小A的任务就是把CPU内核从硅片上切割下来,封装在一个铜盒子里拿出来卖钱。

(自行脑补)

   由于技术原因,CPU的蚀刻有可能失败,如果某个CPU内核蚀刻失败了,它就不能卖钱了。在上图中,有3个内核蚀刻失败,蚀刻失败的内核的坐标分别是C4,F2,G5。显然,如果硅片上有m个CPU内核蚀刻失败了,则有3n2-m个内核是可以卖钱的,如果每个CPU内只装1个内核(这就是传统的单核处理器),此时你就可以做出3n2-m个CPU。
  小A雇用了你来替他完成这个东西。他把所有任务都交给了你。突然,国家发展和改革委员会决定要发展双核处理器技术。你要做的是双核处理器了。上司告诉你,如果在L型硅片上任意两个相邻的CPU内核都是完好的,那么你就可以把它们一起切下来做成一个双核CPU。所谓相邻,指的是CPU内核有一条公共边界。
   由于你的薪水是按照做出的CPU的个数决定的,现在你想算算最多能切出多少个双核的CPU。

输入格式
  第一行包含2个整数n,k
以下K行,每行包括一个字母和紧跟着的一个整数,代表一个蚀刻失败的CPU的坐标。文件保证坐标在L型硅片内。

输出格式
   一行,仅包含一个整数,代表能切出的最多双核CPU个数。

样例输入
2 3
D1
A3
C2

样例输出
4

提示
在100%的数据中2<=n<=7, 0.1n2<=k<=3n^2。

是不是很疑惑,为什么这个人造出来芯片要切开

不然一卖就是一两百核,岂不是666

题解

切芯片嘛,就和切蛋糕一样
这个核和它右边的核在一起了,他就不能和它下面那个核在一起(除非它是个渣核)

那条件就很明显了嘛,不就是 一夫一妻制 嘛
把中心核放一边,把以它为中心的另外4个核放在另外一边
这就是一个小的二分图

那么你把这个图放大一点
那你就会发现这是一个交替放边的图,大意如下(1和0代表放在不同边)

1 0 1 0 1 0 1
0 1 0 1 0 1 0
1 0 1 0 1 0 1
0 1 0 1 0 1 0
1 0 1 0 1 0 1
0 1 0 1 0 1 0
1 0 1 0 1 0 1

那不就是交替染个色,然后同色的放同边呗

构图

易得,相同颜色的横纵坐标之和的奇偶性相同

1(行)+3(列)=4
1(行)+5(列)=6
2(行)+4(列)=6
这些格子同色,且同偶

所以构图代码如下

int A=0,B=0;
for(int a=1;a<=n;a++)
for(int b=1;b<=n;b++)
map[a][b]=((a+b)%2==0)?++A:++B;

读图

由于我们在增广路的时候,只跑同一种颜色的点(就是在同一边的点)
所以我们读图的时候就是跳着读的,就比如上面那个01图,我们只读1或者只读0,这样求出来的才是最大匹配
于是乎
就有了如下代码

for(int x=1;x<=n;x++)
for(int y=1+(x%2);y<=n;y+=2)//跳着读,如果行数x为奇数,就从第二列开始读,为偶数就从第一列开始读,且隔一个读一个
find(maps[x][y])

注意

①构图时要把坏掉的点标记成0
②可以先构一个2n*2n的图,再把(n+1,n+1)到(2n,2n)的点给黑掉

附上代码

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int n,k,res=0;
int fath[1234],went[1234];
int all=0,star[1234],ent[1234],nxt[1234];
int maps[23][23];

void add(int s,int e)
{
    nxt[++all]=star[s];
    star[s]=all;
    ent[all]=e;
}

bool find(int s)
{
    int bian,e;
    for(bian=star[s],e=ent[bian];bian;bian=nxt[bian],e=ent[bian])
    if(!went[e])
    {
        went[e]=1;
        if(!fath[e]||find(fath[e]))
        {
            fath[e]=s;
            return 1;
        }
    }
    return 0;
}

int main()
{
    char x;int y,A=0,B=0;
    scanf("%d%d\n",&n,&k);
    for(int a=1;a<=2*n;a++)
        for(int b=1;b<=2*n;b++)maps[a][b]=(a+b)%2==1?++A:++B;//染色
    while(scanf("%c%d\n",&x,&y)!=EOF)maps[x-'A'+1][y]=0;//删点
    for(int a=n+1;a<=(n<<1);a++)
    for(int b=n+1;b<=(n<<1);b++)
        maps[a][b]=0;
    for(int b=1;b<=(n<<1);b++)
    for(int a=1+b%2;a<=(n<<1);a=a+2)
    if(maps[a][b])
    {
        if(a-1>=1&&maps[a-1][b])add(maps[a][b],maps[a-1][b]);
        if(a+1<=2*n&&maps[a+1][b])add(maps[a][b],maps[a+1][b]);
        if(b+1<=2*n&&maps[a][b+1])add(maps[a][b],maps[a][b+1]);
        if(b-1>=1&&maps[a][b-1])add(maps[a][b],maps[a][b-1]);
    }//构图
    for(int b=1;b<=2*n;b++)
    for(int a=1+b%2;a<=n*2;a=a+2)//读取同色的点
    if(maps[a][b])
    {
        memset(went,0,sizeof(went));
        if(find(maps[a][b]))res++;
    }
    printf("%d",res);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值