【bzoj3698】【XWW的难题】【有上下界的网络流】

探讨了如何对满足特定条件的N*N矩阵进行取整操作,以保持矩阵性质不变的同时使元素总和最大化。利用最大流算法解决该问题。

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

Description

XWW是个影响力很大的人,他有很多的追随者。这些追随者都想要加入XWW教成为XWW的教徒。但是这并不容易,需要通过XWW的考核。
XWW给你出了这么一个难题:XWW给你一个N*N的正实数矩阵A,满足XWW性。
称一个N*N的矩阵满足XWW性当且仅当:(1)A[N][N]=0;(2)矩阵中每行的最后一个元素等于该行前N-1个数的和;(3)矩阵中每列的最后一个元素等于该列前N-1个数的和。
现在你要给A中的数进行取整操作(可以是上取整或者下取整),使得最后的A矩阵仍然满足XWW性。同时XWW还要求A中的元素之和尽量大。

Input

第一行一个整数N,N ≤ 100。
接下来N行每行包含N个绝对值小于等于1000的实数,最多一位小数。

Output

输出一行,即取整后A矩阵的元素之和的最大值。无解输出No。

Sample Input

4
3.1 6.8 7.3 17.2
9.6 2.4 0.7 12.7
3.6 1.2 6.5 11.3
16.3 10.4 14.5 0

Sample Output

129

HINT

【数据规模与约定】

有10组数据,n的大小分别为10,20,30...100。

【样例说明】

样例中取整后满足XWW性的和最大的矩阵为:

3 7 8 18

10 3 0 13

4 1 7 12

17 11 15 0

题解:
            n行n列分别看成n个点,s为源点,t为汇点.
            s向每一行i连a[i][n]的边.
            每一列i向t连a[n][i]的边.
            每一行i向每一行j连a[i][j]的边.
            这里的a值是有取值范围的.所以就是求有源有汇有上下界的最大流.
            具体求法就是
               1.从t向s连一条inf的边,把图变成无源无汇,
               2.新建源汇S,T,按有上下界的方法建图.
               3.求S,T的最大流,检验如果S连出去的边都满流代表合法.
               4.求s,t的最大流即是答案.
           注意最后答案要乘3,因为一个点会在它这个位置,这一行的末尾,这一列的末尾算三次.
 代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 300
#define M 40000
#define inf 210000000
using namespace std;
int point[N],next[M<<1],n,m,sum,s,t,S,T,cnt=1;
int pre[N],gap[N],dis[N],d[N],cur[N];
double a[N][N];
struct use{
  int st,en,v;
}e[M<<1];
void add(int x,int y,int v){
  next[++cnt]=point[x];point[x]=cnt;
  e[cnt].st=x;e[cnt].en=y;e[cnt].v=v;
  next[++cnt]=point[y];point[y]=cnt;
  e[cnt].st=y;e[cnt].en=x;e[cnt].v=0;
}
int isap(int ss,int tt){
  int mn,u=ss,i,ans(0);
  gap[0]=tt;
  for (int i=1;i<=tt;i++) gap[i]=0;
  for (int i=1;i<=tt;i++) dis[i]=0;
  for (int i=1;i<=tt;i++) cur[i]=point[i];
  while (dis[ss]<tt){
    bool f=false;
    for (i=cur[u];i;i=next[i]) 
      if (e[i].v&&dis[e[i].en]+1==dis[u]){f=true;cur[u]=i;break;}
    if (f){
      pre[u=e[i].en]=i;
      if (u==tt){ 
        mn=inf;
        for (int i=tt;i!=ss;i=e[pre[i]].st) mn=min(mn,e[pre[i]].v);
        ans+=mn;
        for (int i=tt;i!=ss;i=e[pre[i]].st) e[pre[i]].v-=mn,e[pre[i]^1].v+=mn;
        u=ss;
      }
    }
    else{
      gap[dis[u]]--;if (!gap[dis[u]]) return ans;
      for (mn=tt,i=point[u];i;i=next[i]) if (e[i].v) mn=min(mn,dis[e[i].en]);
      gap[dis[u]=mn+1]++;cur[u]=point[u];if (u!=ss) u=e[pre[u]].st;
    }
  }
  return ans;
}
void build(){
  add(t,s,inf);
  for (int i=1;i<n;i++){
    if (a[i][n]!=(int)a[i][n]) add(s,i,1);
    d[s]-=(int)a[i][n];d[i]+=(int)a[i][n];
  }
  for (int i=1;i<n;i++){
    if (a[n][i]!=(int)a[n][i])add(i+n,t,1);
      d[t]+=(int)a[n][i];d[i+n]-=(int)a[n][i];
  }  
  for (int i=1;i<n;i++)
    for (int j=1;j<n;j++){
      if (a[i][j]!=(int)a[i][j]) add(i,j+n,1);
      d[i]-=(int)a[i][j];d[j+n]+=(int)a[i][j];
    }
  for (int i=1;i<=t;i++)
    if (d[i]>0) add(S,i,d[i]),sum+=d[i];
    else if (d[i]<0) add(i,T,-d[i]);
}
int main(){
  //freopen("a.in","r",stdin);
  //freopen("a.out","w",stdout);
  scanf("%d",&n);
  for (int i=1;i<=n;i++)
    for (int j=1;j<=n;j++)
      scanf("%lf",&a[i][j]);
  s=2*n+1,t=s+1;S=t+1;T=S+1;
  build();
  if (isap(S,T)!=sum){printf("No\n");return 0;};
  printf("%d\n",isap(s,t)*3);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值