bzoj3698: XWW的难题 //有上下界有源汇最大流

bzoj3698: XWW的难题


题意

给出一个N(<=100)*N正实数矩阵,保证除最后一行,每一行的最后一个数等于这一行前面的数之和;除最后一列,每一列的最后一个数等于这一列前面的数之和。要求对矩阵里的每一个元素进行上/下取整,使取整后矩阵依然满足该性质且元素和最大。


题解

考虑每行每列建点的经典建图。
源点向每一行连边,下界为该行元素和下取整,上界为该行元素和上取整。
每一列同理向汇点连边。
每一行向每一列连边,下界为该位置的数下取整,上界为该位置的数上取整。
然后跑一下上下界最大流就好。


代码

调了好长时间,发现实数上下取整的时候出现了奇怪的问题…
所以写了一个比较难看的read

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 210
#define M 20500
#define inf 0x3f3f3f3f
using namespace std;
int n,m,l,r,deg[N],S,T,SS,TT,Cnt,ans;
char c[7];
double a;
int to[M],hd[M],val[M],lk[N],cnt=1;
inline int read()
{
    scanf("%s",c);
    int tp=strlen(c),ret=0;
    bool f=0;
    for(int i=0;i<tp;i++)
    if(c[i]!='.')ret=ret*10+c[i]-'0';
    else f=1;
    if(!f)ret*=10;return ret;
}
inline void add(int u,int v,int w)
{
    to[++cnt]=v,hd[cnt]=lk[u],val[cnt]=w,lk[u]=cnt,
    to[++cnt]=u,hd[cnt]=lk[v],lk[v]=cnt;
}
int q[N],h,t,dis[N],cur[N];
bool bfs(bool mode)
{
    int x;
    for(int i=0;i<=TT;i++)
    dis[i]=0,cur[i]=lk[i];
    dis[q[h=0]=(mode?S:SS)]=t=1;
    while(h<t)
    {
        x=q[h++];
        for(int k,i=lk[x];i;i=hd[i])
        if(val[i]&&!dis[k=to[i]])
        dis[q[t++]=k]=dis[x]+1;
    }
    return dis[mode?T:TT];
}
int dfs(int x,int v,int mode)
{
    if(x==(mode?T:TT))return v;
    int ret=0,cst;
    for(int k,&i=cur[x];i;i=hd[i])
    if(dis[k=to[i]]==dis[x]+1&&val[i])
    {
        cst=dfs(k,min(v,val[i]),mode);
        v-=cst,ret+=cst,val[i]-=cst,val[i^1]+=cst;
        if(!v)break;
    }
    return ret;
}int u,v;
int main()
{
    scanf("%d",&n);T=(S=((n-1)<<1))+1,TT=(SS=T+1)+1;
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    {
        m=read();
        l=m/10,r=(m+9)/10;
        if(i<n-1)
        {
            if(j<n-1)u=i,v=n+j-1;
            else u=S,v=i;
        }
        else if(j<n-1)u=n+j-1,v=T;
        else continue;
        if(l<r)add(u,v,1);
        deg[u]+=l,deg[v]-=l;
    }
    Cnt=cnt,m=0;
    for(int i=0;i<=T;i++)
    if(deg[i]<0)
    add(SS,i,-deg[i]);
    else add(i,TT,deg[i]),m+=deg[i];
    add(T,S,inf);
    while(bfs(0))ans+=dfs(SS,inf,0);
    if(ans<m)
    {puts("No");return 0;}
    ans=0;
    for(int i=lk[SS];i;i=hd[i])
    val[i]=val[i^1]=0;
    for(int i=lk[TT];i;i=hd[i])
    val[i]=val[i^1]=0;
    while(bfs(1))ans+=dfs(S,inf,1);
    printf("%d",ans*3);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值