[BZOJ2406]矩阵(二分+有源汇有上下界的可行流)

本文介绍了一种使用二分法结合网络流算法解决特殊矩阵构造问题的方法。该问题要求构造一个矩阵,使得其每行和每列的和在一定范围内变动,目标是最小化这些和的绝对值的最大值。通过将问题转化为有上下界限制的网络流问题,并利用ISAP算法进行求解。

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

题目描述

传送门

题解

刚开始没看见绝对值。。。
把这道题翻译一下其实就是构造一个b矩阵,其中每一个点有限制[L,R],令矩阵c=a-b,使c矩阵每一行的和的绝对值和每一列的和的绝对值的最大值最小

最大值最小很容易想到二分
二分答案mid之后,用网络流判定
就是满足 |aibi|mid
分类讨论一下得出 aimidbiai+mid
然后就很容易看出是一个有上下界的网络流了
原图:
每一行和每一列建一个点xi,yi
s->xi,[ai-mid,ai+mid]
yj->t,[aj-mid,aj+mid]
xi->yj,[L,R]
只要判断是否有可行流就行了
按照有源汇有上下界的可行流将原图改造求解即可

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
#define N 410
#define E 100005
#define inf 2000000000

int n,m,s,t,ss,tt,L,R,in,out,maxflow,ans;
int a[N][N],line[N],lie[N];
int tot,point[N],nxt[E],v[E],remain[E];
int d[N],deep[N],last[N],num[N],cur[N];
queue <int> q;

void addedge(int x,int y,int cap)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=cap;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;
}
void bfs(int t)
{
    for (int i=1;i<=t;++i) deep[i]=t;
    deep[t]=0;
    for (int i=1;i<=t;++i) cur[i]=point[i];
    while (!q.empty()) q.pop();
    q.push(t);
    while (!q.empty())
    {
        int now=q.front();q.pop();
        for (int i=point[now];i!=-1;i=nxt[i])
            if (deep[v[i]]==t&&remain[i^1])
            {
                deep[v[i]]=deep[now]+1;
                q.push(v[i]);
            }
    }
}
int addflow(int s,int t)
{
    int ans=inf,now=t;
    while (now!=s)
    {
        ans=min(ans,remain[last[now]]);
        now=v[last[now]^1];
    }
    now=t;
    while (now!=s)
    {
        remain[last[now]]-=ans;
        remain[last[now]^1]+=ans;
        now=v[last[now]^1];
    }
    return ans;
}
void isap(int s,int t)
{
    bfs(t);
    for (int i=1;i<=t;++i) ++num[deep[i]];

    int now=s;
    while (deep[s]<t)
    {
        if (now==t)
        {
            maxflow+=addflow(s,t);
            now=s;
        }

        bool has_find=false;
        for (int i=cur[now];i!=-1;i=nxt[i])
            if (deep[v[i]]+1==deep[now]&&remain[i])
            {
                has_find=true;
                cur[now]=i;
                last[v[i]]=i;
                now=v[i];
                break;
            }

        if (!has_find)
        {
            int minn=t-1;
            for (int i=point[now];i!=-1;i=nxt[i])
                if (remain[i]) minn=min(minn,deep[v[i]]);
            if (!(--num[deep[now]])) break;
            ++num[deep[now]=minn+1];
            cur[now]=point[now];
            if (now!=s) now=v[last[now]^1];
        }
    }
}
bool check(int mid)
{
    tot=-1;memset(point,-1,sizeof(point));
    memset(num,0,sizeof(num));
    memset(d,0,sizeof(d));
    in=out=0;maxflow=0;
    for (int i=1;i<=n;++i)
    {
        addedge(s,i,mid+mid);
        d[s]-=line[i]-mid,d[i]+=line[i]-mid;
    }
    for (int i=1;i<=m;++i)
    {
        addedge(n+i,t,mid+mid);
        d[n+i]-=lie[i]-mid,d[t]+=lie[i]-mid;
    }
    for (int i=1;i<=n;++i)
        for (int j=1;j<=m;++j)
        {
            addedge(i,n+j,R-L);
            d[i]-=L,d[n+j]+=L;
        }
    addedge(t,s,inf);
    for (int i=1;i<=t;++i)
    {
        if (d[i]>0) addedge(ss,i,d[i]),in+=d[i];
        if (d[i]<0) addedge(i,tt,-d[i]),out-=d[i];
    }
    if (in!=out) return 0;
    isap(ss,tt);
    return maxflow==in;
}
int find()
{
    int l=0,r=2000000,mid,ans=2000000;
    while (l<=r)
    {
        mid=(l+r)>>1;
        if (check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    s=n+m+1,t=s+1,ss=t+1,tt=ss+1;
    for (int i=1;i<=n;++i)
        for (int j=1;j<=m;++j)
        {
            scanf("%d",&a[i][j]);
            line[i]+=a[i][j];
            lie[j]+=a[i][j];
        }
    scanf("%d%d",&L,&R);
    ans=find();
    printf("%d\n",ans);
}
### BZOJ 中关于宽度优先搜索的相关题目 在BZOJ平台中,涉及宽度优先搜索算法的题目广泛存在于路径寻找、连通性分析以及层次遍历等问题场景下。虽然提供的参考资料并未直接提及具体的宽搜题目列表[^2],但基于此平台的特点和常见题型分布情况,可以列举一些典型的宽搜应用实例。 #### 题目示例 1. **迷宫问题** 迷宫问题是经典的宽搜应用场景之一,在此类题目中通常会给出一个二维网格表示的地图,其中某些位置不可通行,目标是从起点到达终点并计算最短路径长度或判断是否存在可行路线。 2. **图的连通分量统计** 给定无向图G=(V,E),通过执行多次宽搜可以从不同未访问节点出发探索所有可达顶点集合,进而确定图中有多少个独立子图及其各自规模大小。 3. **最近公共祖先查询** 对于一棵树结构而言,利用宽搜能够高效处理多个结点间LCA(Lowest Common Ancestor)关系判定任务,尤其适用于在线询问模式下的快速响应需求。 4. **洪水填充游戏(Flood Fill Game)** 此类题目要求按照指定颜色替换相邻相同色块直至无法继续扩展为止;采用宽搜策略有助于实现逐层扩散染色过程,并保证操作效率最优。 5. **棋盘覆盖问题** 当面对特定形状障碍物存在条件下的n*n国际象棋棋盘时,如何用最少数量的标准多米诺骨牌完全铺满剩余空白区域成为了一道有趣的挑战——借助宽搜思路可以帮助找到合理的放置顺序与组合方式。 以上仅是对可能出现在BZOJ上的部分宽搜相关题目的概括描述,实际参赛者还需要根据具体题目描述深入理解背景设定及解法细节。 ```cpp // C++ BFS Template Code Example #include <queue> using namespace std; void bfs(int start){ queue<int> q; bool visited[MAX_SIZE]; memset(visited,false,sizeof(visited)); q.push(start); visited[start]=true; while(!q.empty()){ int current=q.front(); q.pop(); // Process node 'current' for(auto neighbor : adj[current]){ if(!visited[neighbor]){ q.push(neighbor); visited[neighbor]=true; // Additional processing as needed... } } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值