【题解】方格取数&&骑士共存问题(二分图最大独立集)

本文介绍如何在网格模型下,通过构建二分图并求最大流解决方格取数与骑士共存问题。核心思路是利用点的排斥关系,通过染色区分黑白点,建立超级源点和汇点,最终求最小割获取最大独立集。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

方格取数问题传送门

骑士共存问题传送门

分析

之所以把这两题放在一起,是因为它们有很多的共同点。

单看题目背景就知道其实它们挺类似的:都是在棋盘(网格)中,且点与点之间有相互的排斥关系。实际上它们的解法思想也几乎一模一样。

方格取数

先讲方格取数。

首先我们发现这类问题的最重要的特点:点与点之间会互相排斥,且每个点只有选与不选两种状态。于是我们可以考虑构建二分图,并建立超级源点 s ​ s​ s和汇点 t ​ t​ t,最后求最小割(最大流)。具体建立过程如下:

  1. 首先把网格进行黑白染色。比如把横纵坐标和为奇数的染成黑色,和为偶数的染成白色,于是相邻的黑白点就不能同时被选。这一步并不需要真正用代码进行染色,只需要在建图的时候判断横纵坐标和即可,同时也是方便后面理解。
  2. 于是我们考虑把所有黑点放在二分图的左侧,如果一个黑点与 s s s有连边,就代表它被选。同理把白点放在右边,如果与 t t t连边,就代表白点被选。考虑边的权值,显然如果一个点(无论黑白)被选,价值就是当前点的点权,于是黑白点向 s s s t t t连的边边权都等于点权。
  3. 接着考虑限制条件,显然如果我们要让两个点中最多选一个,那么我们肯定要让其中一个点向 s s s t t t的连边在求最小割的时候被割掉(可以重新看看2中的定义)。于是我们对于每个黑点,向受其影响的白点连边权为 I N F INF INF的边,由于最小割的性质,边权为 I N F INF INF的边不可能被割掉。
  4. 跑最小割(最大流)模板,最后的答案为点权总和减去最小割。

代码(建图)

Show You the Code.

for(int i = 1; i <= m; i++){
    for(int j = 1; j <= n; j++){
        scanf("%d", &a[i][j]);
        sum += a[i][j];
        if((i+j)&1){		//判断黑白点
            add(s, get(i, j), a[i][j]);
            add(get(i, j), s, 0);
        }
        else{
            add(get(i, j), t, a[i][j]);
            add(t, get(i, j), 0);
        }
    }
}
for(int i = 1; i <= m; i++){
    for(int j = 1; j <= n; j++){
        for(int k = 0; k < 4; k++){		//四个方向都会影响到
            if(!((i+j)&1)) continue;
            int u = i+mx[k], v = j+my[k];
            if(u<=0 || u>m || v<=0 || v>n) continue;
            add(get(i, j), get(u, v), INF);	//get(x,y)把二维坐标转化为一位编号. get(x,y)=(n-1)*x+y
            add(get(u, v), get(i, j), 0);
        }
    }
}
dinic();
cout << sum-ans << endl;	//ans是最大流

小小的小结

回过头来看看我们在这道题中建图后的本质操作是什么。

我们考虑最后被割成两半的图,图中互相影响的点一定不再同一个集合中,而我们建图的时候是有影响就建边,所以其实我们是从初始的二分图中找出了最大的独立集(也就是最大的点集,其中的点两两之间都没有连边)。


骑士共存

(讲完上一题应该都会骑士共存了)

同样的,我们发现虽然影响的方式变了,但是假如我们依旧用同样的方式染色,黑白点仍旧只会互相影响,而不会直接影响同色的点(可以看题目中的图)。这说明我们其实完全可以按照一样的套路来乱搞。

这道题多了障碍,实际上并没用,遇到障碍的时候直接跳过不连边就行了。

代码(还是建图)

for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= n; ++j) {
        if(mp[i][j]) continue;		//mp[i][j]为1则表示有障碍
        if((i+j)&1){
            add(s, get(i,j), 1);		//点权实际可以看做1
            add(get(i,j), s, 0);
        }
        else{
            add(get(i,j), t, 1);
            add(t, get(i,j), 0);
        }
    }
}
for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= n; ++j) {
        if(mp[i][j] || !((i+j)&1)) continue;
        int u, v;
        for (int k = 0; k < 8; ++k) {
            u = i+mx[k], v = j+my[k];
            if(u<=0 || u>n || v<=0 || v>n || mp[u][v]) continue;
            add(get(i, j), get(u, v), INF);
            add(get(u, v), get(i, j), 0);
        }
    }
}
dinic();
cout << n*n-m-ans << endl;		//总共n*n个点,去掉m个障碍,再减去最大流

真正的总结

我们可以发现在类似这样的网格模型下,只要按题意建图,然后求最大独立集即可。(好像比小结还短)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值