UVALive 5131 Chips Challenge 费用流

题意:
有一个芯片,芯片上有N*N(1≤N≤40)个插槽,可以在里面装零件。
有些插槽不能装零件,有些插槽必须装零件,剩下的插槽随意。
要求装好之后满足如下两条要求:
1、第 i 行和第 i 列的零件数目必须一样多(1≤i≤N)。
2、第 i 行的零件数目不能超过总的零件数目的 A/B(1≤i≤N,0≤A≤B≤1000,B≠0)。
求最多可以另外放多少个零件(就是除掉必须放的)。如果无解输出impossible。
分析:
这题挺神的。。太难想了。
先看第2个限制,这个怎么搞?其实很显然的第i行最多放n个零件,于是枚举每一行最多放0个零件,放1个零件。。。放n个零件,这个就是所有可能了,然后对每一种可能用第2个限制判断一下,不符合就不更新答案。
再看第一个限制,要求一样多,这个又怎么做?
我是想到这卡住了。。看了下别人的题解。
这里可以倒过来想,就是假设所有的可以放零件的都放,然后为了让方案合法要删除一些零件。
假设第i行第i列都可以放最多x个零件,那么就先从源点流x的流量到第i行,然后假设现在枚举到了最多放k个零件:
1.k小于x,说明流多了,但是已经流到第i行了,那就分配k的流量流向第i列,这样保证放的零件会相等,然后x-k个流量表示x-k个格子只有不选了,如果第i行第j列这个格子可以不选,那么从第i行向第j列连一条流量为1的边表示这个格子不选。为了统计这种不选的格子的数量,那么要加一个费用。
2.k大于等于x,那么就从源点流向第i行x个流量。
刚才分析了一下网络流是怎么流的,表示了什么,接下来就可以总结出连边方法了。
从源点连边向每一行,流量为这一行最多放的零件(包括可以放和必须放的)数量,费用为0,从每一列连边向汇点,流量为这一列最多放的零件(包括可以放和必须放的)数量,费用为0,从第i行连边向第i列,流量为枚举的那个值,费用为0,如果第i行第j列的格子可以放零件,就从第i行向第j列连边,流量为1,费用为1。
然后跑最小费用流。
然后看流量,必须满流,不然不符题意。
满流了看费用,也就是不放的个数,然后用第二个限制条件搞一下看是不是满足题意,然后更新一下答案。
附代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
struct node{
    int v,next,cap,cost;
}edge[50010];
int n,a,b,s,t,ans,cnt,sum,used,cas,fare,flow,row[44],col[44],dis[88],head[88];
char mp[44][44];
bool inq[88],vis[88];
void addedge(int u,int v,int cap,int cost)
{
    edge[cnt].v=v;
    edge[cnt].cost=cost;
    edge[cnt].cap=cap;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
void add(int u,int v,int cap,int cost)
{
    addedge(u,v,cap,cost);
    addedge(v,u,0,-cost);
}
bool spfa()
{
    queue<int>q;
    q.push(t);
    memset(dis,0x3f,sizeof dis);
    dis[t]=0;
    inq[t]=1;
    while(!q.empty())
    {
        int u=q.front();
        inq[u]=0;
        q.pop();
        for(int i=head[u];~i;i=edge[i].next)
        {
            int v=edge[i].v;
            if(edge[i^1].cap>0&&dis[v]>edge[i^1].cost+dis[u])
            {
                dis[v]=edge[i^1].cost+dis[u];
                if(!inq[v])inq[v]=1,q.push(v);
            }
        }
    }
    return dis[s]!=inf;
}
int aug(int u,int augco)
{
    if(u==t)
    {
        fare+=dis[s]*augco;
        return augco;
    }
    int delta,augc=augco;
    vis[u]=1;
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].v;
        if(edge[i].cap>0&&!vis[v]&&dis[u]==dis[v]+edge[i].cost)
        {
            delta=aug(v,min(augc,edge[i].cap));
            edge[i].cap-=delta;
            edge[i^1].cap+=delta;
            augc-=delta;
        }
    }
    return augco-augc;
}
int main()
{
    while(scanf("%d%d%d",&n,&a,&b)&&(n+a+b))
    {
        memset(col,0,sizeof col);
        memset(row,0,sizeof row);
        sum=used=0;
        ans=-1;
        for(int i=1;i<=n;++i)
        {
            scanf("%s",mp[i]+1);
            for(int j=1;j<=n;++j)
            {
                if(mp[i][j]=='C'||mp[i][j]=='.')
                {
                    ++sum;
                    ++row[i];
                    ++col[j];
                    if(mp[i][j]=='C')++used;
                }
            }
        }
        for(int f=0;f<=n;++f)
        {
            memset(head,-1,sizeof head);
            flow=cnt=fare=s=0;
            t=n*2+1;
            for(int i=1;i<=n;++i)
            {
                add(s,i,row[i],0);
                add(i+n,t,col[i],0);
                add(i,i+n,f,0);
                for(int j=1;j<=n;++j)
                    if(mp[i][j]=='.')
                        add(i,j+n,1,1);
            }
            while(spfa())
            {
                memset(vis,0,sizeof vis);
                vis[s]=1;
                flow+=aug(s,inf);
            }
            if(flow==sum&&f*b<=(sum-fare)*a)
                ans=max(ans,sum-fare);
        }
        printf("Case %d: ",++cas);
        if(ans==-1)puts("impossible");
        else printf("%d\n",ans-used);
    }
}

你可能感兴趣的:(UVALive 5131 Chips Challenge 费用流)