World final 2012 (Chips Challenge-无源汇最小费用流)

每块芯片都被设计成N*N( N<=40) 的带插槽的正方形。
你需要满足限制如下:
1. 部分插槽不可用
2. 部分插槽已经被组件占据
3. 第i行和第i列组件数目必须相同
4. 对于给出的 A,B ,任何行和列的组件总数不超过芯片上组数的A/B
问最多能再放几个组件。

枚举答案,对于每个答案建立二分图,为了保证条件(3),将右边节点向左边节点连边,如果有解,则
- totcost/inf=C的个数
- totcost%inf=放组件的个数

建图,根据doc老师建的四分图再改成二分图后,
我们得到了这样的无缘无汇最小费用循环流。

Chips Challenge

无缘无汇最小费用循环流求解:查询图中有无负环,有则满流,无则继续在残余网络上找,直至无负环为止。
spfa时先把所有点压进队列,并假设 di=0 ,执行SPFA观察是否有点进队超过 n2 次,这样期望找出负环的时间复杂度大致为 O(n2logn) (我也不确定)。
由于每次负环最优查询为 O(n2) ,大概可以证明这个程序复杂度 O(n5)

#include<bits/stdc++.h> 
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define ForkD(i,k,n) for(int i=n;i>=k;i--)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=Pre[x];p;p=Next[p])
#define Forpiter(x) for(int &p=iter[x];p;p=next[p])  
#define Lson (o<<1)
#define Rson ((o<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,0x3f,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define MEMx(a,b) memset(a,b,sizeof(a));
#define INF (0x3f3f3f3f)
#define F (1000000007)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define vi vector<int> 
#define pi pair<int,int>
#define SI(a) ((a).size())
#define Pr(kcase,ans) printf("Case #%d: %lld\n",kcase,ans);
#define PRi(a,n) For(i,n-1) cout<<a[i]<<' '; cout<<a[n]<<endl;
#define PRi2D(a,n,m) For(i,n) { \
                        For(j,m-1) cout<<a[i][j]<<' ';\
                        cout<<a[i][m]<<endl; \
                        } 
#pragma comment(linker, "/STACK:102400000,102400000")
#define ALL(x) (x).begin(),(x).end()
#define gmax(a,b) a=max(a,b);
#define gmin(a,b) a=min(a,b);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
ll mul(ll a,ll b){return (a*b)%F;}
ll add(ll a,ll b){return (a+b)%F;}
ll sub(ll a,ll b){return ((a-b)%F+F)%F;}
void upd(ll &a,ll b){a=(a%F+b%F)%F;}
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
    while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
    return x*f;
} 
int n,a,b; 
char s[50][50];
int col[50],row[50];
#define MAXN (210)
#define MAXM ((40+40+40*40+40)*2+100)
class Cost_Flow  
{  
public:  
    int n,s,t;  
    int edge[MAXM],Next[MAXM],Pre[MAXN],weight[MAXM],size;  
    int cost[MAXM];  
    void addedge(int u,int v,int w,int c)    
    {    
        edge[++size]=v;    
        weight[size]=w;    
        cost[size]=c;    
        Next[size]=Pre[u];    
        Pre[u]=size;    
    }    
    void addedge2(int u,int v,int w,int c=0){addedge(u,v,w,c),addedge(v,u,0,-c);}   
    bool b[MAXN];  
    int d[MAXN],inq[MAXN];  
    int pr[MAXN],ed[MAXN]; 
    queue<int> q;
    int Find_Negative_Circle()    
    {    
        while(!q.empty()) q.pop(); 
        For(i,n) d[i]=inq[i]=0;
        For(i,n) q.push(i),inq[i]=1;

        int nowv=0;
        while(!q.empty()) {
            int now=q.front();q.pop();
            Forp(now) {
                int v=edge[p];
                if(weight[p]&&d[now]+cost[p]<d[v]) {
                    d[v]=d[now]+cost[p];
                    inq[v]++;
                    if (inq[v]>n) {
                        nowv=v;
                        break;
                    }
                    q.push(v);    
                    pr[v]=now,ed[v]=p;    
                }
            }
            if (nowv) break;
        }
        if (!nowv) return 0;
        MEM(b)
        for(nowv;!b[nowv];nowv=pr[nowv]) {
            b[nowv]=1;nowv=pr[nowv]; 
        } 
        return nowv;
    }
    int totcost;    
    int CostFlow()    
    {
        int nowv=0,maxflow=0;
        while (nowv=Find_Negative_Circle())    
        {    
            int flow=INF,c=0,x=nowv;    
            do  {
                c+=cost[ed[x]];
                flow=min(flow,weight[ed[x]]);
                x=pr[x];
            } while(x!=nowv);
            if (!c) break;
            totcost+=flow*c; 
            maxflow+=flow;   
            x=nowv;
            do  {
                weight[ed[x]]-=flow,weight[ed[x]^1]+=flow;
                x=pr[x];
            }while(x!=nowv);
        }    
        return totcost;    
    }    

    void mem(int n,int s,int t)  
    {  
        (*this).n=n;  (*this).s=s;(*this).t=t;
        size=1;  
        totcost=0;  
        MEM(Pre) MEM(Next)   
    }  
}S;  
int idr[MAXN],idc[MAXN];
int main()
{
//  freopen("A.in","r",stdin);
//  freopen(".out","w",stdout);
    int kcase=1;
    while(scanf("%d%d%d",&n,&a,&b)==3 && n) {
        MEM(col) MEM(row)
        int L=0,R=n*n;
        For(i,n) {
            scanf("%s",s[i]+1);
            For(j,n) {
                if (s[i][j]=='C') row[i]++,col[j]++,L++;
                if (s[i][j]=='/') --R;
                if (s[i][j]=='.') row[i]++,col[j]++;
            }
        }
        int ans=-1;
        R=min(R,n*n*a/b);
        Fork(f,L,R){
            int maxl=f*a/b;
            if (f>L) {
                if (maxl!=(f-1)*a/b) {
                    For(i,n) {
                        int l1=min(maxl,min(col[i],row[i]))-min((f-1)*a/b,min(col[i],row[i]));
                        S.weight[idr[i]]+=l1;
                    }   
                }
            } else {
                S.mem(2*n,2*n,2*n);
                For(i,n) {
                    S.addedge2(n+i,i,min(maxl,min(col[i],row[i])));
                    idr[i]=S.size-1;
                }
                For(i,n) For(j,n) {
                    if (::s[i][j]=='C') {
                        S.addedge2(i,n+j,1,-1e6);
                    } else if (::s[i][j]=='.'){
                        S.addedge2(i,n+j,1,-1);
                    }
                }
            }
             int p=S.CostFlow();
             if (p==L-f-L*1e6) {
                ans=max(ans,f-L);
            }
        }
        printf("Case %d: ",kcase++);
        if (ans^-1) printf("%d\n",ans);
        else puts("impossible");
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值