HDU 4862 最小费用最大流,判满流


题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4862


主要是构图,然后跑一下最小费用最大流,判断是不是满流,是的话答案就是费用的相反数,否则就为-1


构造二部图,X部n*m个点表示从[i,j]这个点走到Y部的点,Y部n*m个点表示从X部的点走到[i,j]

从源点到X部的每一个点连一条边,流量为1,费用为0,从Y部的每一个点到汇点连一条边,流量为1,费用为0

从X部的每一个点到Y部它能一步到达的点连一条边,流量为1,费用为-(获得的能量-花费),因为求最大值,所以取反

在X部添加一个点p,从源点到p连一条边,流量为K,费用为0,从p到Y部每一个点连一条边,流量为1,费用为0

表示其中的K个点可以为起点而不用从其他的点到达


#include <iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#define INF 0x7fffffff
#define MAXN 505
#include<queue>
#define MAXE 10005
using namespace std;
int net[MAXN],size;
struct EDGE{
    int v,next,cap,cost;
}e[MAXE];
void init(){
    size=0;
    memset(net,-1,sizeof(net));
}
void addedge(int u,int v,int cap,int cost){
    e[size].v=v;e[size].cap=cap;e[size].cost=cost;e[size].next=net[u];net[u]=size++;
    e[size].v=u;e[size].cap=0;e[size].cost=-cost;e[size].next=net[v];net[v]=size++;
}
int nv,dist[MAXN],pre[MAXN],pe[MAXN];
bool hash[MAXN];
queue<int> q;
bool spfa(int s,int t){
    while(!q.empty())
    q.pop();
    memset(hash,0,sizeof(hash));
    memset(pre,-1,sizeof(pre));
    for(int i=0;i<nv;i++)
    dist[i]=INF;
    dist[s]=0;
    hash[s]=1;
    q.push(s);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        hash[u]=0;
        for(int i=net[u];i!=-1;i=e[i].next){
            int v=e[i].v;
            if(e[i].cap&&dist[v]>dist[u]+e[i].cost){
                dist[v]=e[i].cost+dist[u];
                pre[v]=u;
                pe[v]=i;
                if(hash[v]==0){
                    hash[v]=1;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t]==-1)
    return false;
    return true;
}
int MCMF(int s,int t,int need){
    int max_flow=0;
    int min_cost=0;
    while(spfa(s,t)){
        int aug=INF;
        for(int v=t;v!=s;v=pre[v]){
            aug=min(aug,e[pe[v]].cap);
            min_cost+=e[pe[v]].cost;
        }
        max_flow+=aug;
        for(int v=t;v!=s;v=pre[v]){
            e[pe[v]].cap-=aug;
            e[pe[v]^1].cap+=aug;
        }
    }
    if(max_flow<need)
    return -1;
    return -min_cost;
}
int main()
{
    int t,n,m,ics,k;
    char str[15];
    int mp[13][13];
    scanf("%d",&t);
    for(ics=1;ics<=t;ics++){
        scanf("%d%d%d",&n,&m,&k);
        init();
        for(int i=0;i<n;i++){
            scanf("%s",str);
            for(int j=0;j<m;j++){
                mp[i][j]=str[j]-'0';
            }
        }
        int tmp;
        int s=n*m*2,t=n*m*2+1,p=n*m*2+2;
        nv=p+1;
        addedge(s,p,k,0);
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                addedge(s,i*m+j,1,0);
                addedge(n*m+i*m+j,t,1,0);
                addedge(p,n*m+i*m+j,1,0);
                for(int k=j+1;k<m;k++){
                    tmp=mp[i][j]==mp[i][k]?mp[i][j]:0;
                    addedge(i*m+j,n*m+i*m+k,1,-tmp+(k-j)-1);
                }
                for(int k=i+1;k<n;k++){
                    tmp=mp[i][j]==mp[k][j]?mp[i][j]:0;
                    addedge(i*m+j,n*m+k*m+j,1,-tmp+(k-i)-1);
                }
            }
        }
        printf("Case %d : %d\n",ics,MCMF(s,t,n*m));

    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值