洛谷 P3973 [TJOI2015]线性代数 最小割

本文探讨了如何利用最小割算法解决一个特定的线性代数问题,即找到一个01矩阵A,使表达式D=(A×B−C)×AT达到最大值。通过对题目的数学公式进行分析,将问题转化为最小割问题,并提供了详细的算法实现过程。

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

题目描述

为了提高智商,ZJY开始学习线性代数。她的小伙伴菠萝给她出了这样一个问题:给定一个n×nn×nn×n的矩阵BBB和一个1×n1×n1×n的矩阵CCC。求出一个1×n1×n1×n的01矩阵AAA。使得D=(A×B−C)×ATD=(A×B-C)×A^{\sf T}D=(A×BC)×AT最大,其中ATA^{\sf T}ATAAA的转置。输出DDD

输入输出格式

输入格式:
第一行输入一个整数n。接下来n行输入B矩阵,第i行第j个数代表B接下来一行输入n个整数,代表矩阵C。矩阵B和矩阵C中每个数字都是不过1000的非负整数

输出格式:
输出一个整数,表示最大的D。

输入输出样例

输入样例#1:
3
1 2 1
3 1 0
1 2 3
2 3 7
输出样例#1:
2
说明

数据范围

对于 30% 的数据,1 ≤ n ≤ 15

对于 100% 的数据,1 ≤ n ≤ 500

分析:
化一下式子得到,
∑i=1n∑j=1nai∗aj∗bi,j−∑i=1nai∗ci\sum_{i=1}^{n}\sum_{j=1}^{n}a_i*a_j*b_{i,j}-\sum_{i=1}^{n}a_i*c_ii=1nj=1naiajbi,ji=1naici
也就是aia_iai111−ci-c_ici的价值,而同时选了aia_iaiaja_jaj就有bi,i+bi,j+bj,i+bj,jb_{i,i}+b_{i,j}+b_{j,i}+b_{j,j}bi,i+bi,j+bj,i+bj,j的价值。
显然可以最小割解决。

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>

const int maxn=517;
const int inf=0x3f3f3f3f;

using namespace std;

int n,s,t,ans,cnt;
int b[maxn][maxn],c[maxn],dis[maxn],ls[maxn];

struct edge{
    int y,w,op,next;
}g[maxn*maxn*2];

queue <int> q;

void add(int x,int y,int w)
{
    g[++cnt]=(edge){y,w,cnt+1,ls[x]};
    ls[x]=cnt;
    g[++cnt]=(edge){x,0,cnt-1,ls[y]};
    ls[y]=cnt;
}

bool bfs()
{
    while (!q.empty()) q.pop();
    for (int i=s;i<=t;i++) dis[i]=inf;
    dis[s]=0;
    q.push(s);
    while (!q.empty())
    {
        int x=q.front();
        q.pop();
        for (int i=ls[x];i>0;i=g[i].next)
        {
            int y=g[i].y;
            if ((g[i].w) && (dis[y]>dis[x]+1))
            {
                dis[y]=dis[x]+1;
                if (y==t) return 1;
                q.push(y);
            }
        }
    }
    return 0;
}

int dfs(int x,int maxf)
{
    if (x==t) return maxf;
    int ret=0;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if ((g[i].w) && (dis[y]==dis[x]+1))
        {
            int f=dfs(y,min(maxf-ret,g[i].w));
            if (!f) dis[y]=-1;
            g[i].w-=f;
            g[g[i].op].w+=f;
            ret+=f;
            if (ret==maxf) break;
        }
    }
    return ret;
}

int dinic()
{
    int flow=0;
    while (bfs()) flow+=dfs(s,inf);
    return flow;
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++) scanf("%d",&b[i][j]);
    }
    for (int i=1;i<=n;i++) scanf("%d",&c[i]);	
    s=0,t=n+1;
    for (int i=1;i<=n;i++)
    {
        int sum=0;
        for (int j=1;j<=n;j++)
        {
            sum+=b[i][j];
            sum+=b[j][i];
            ans+=b[i][j];
        }
        add(s,i,sum);
        add(i,t,2*c[i]);
    }
    for (int i=1;i<=n;i++)
    {
        for (int j=i+1;j<=n;j++) add(i,j,b[i][j]+b[j][i]),add(j,i,b[i][j]+b[j][i]);
    }	
    ans-=dinic()/2;
    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值