hdu4862(14多校第一场B题)及最小费用最大流模板

解题思路:

建立一个流量网络,一个二部图。X部分向Y部分链接的情况表示可以从一个点跳到另一个点,超级源点和超级汇点分别同X部分的点和Y部分的点链接。在X部分中多加一个点它与源点的流量是K费用是0,与Y部分所有点链接流量是1费用是0。这表示操作K次。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define rep(i,a,b)	for (int i=a;i<(b+1);i++)
using namespace std;
const int maxn=250;
const int inf=1<<29;
const int maxm=maxn*maxn*5;
int n,m,k,st,end,head[maxn]={0},pnt[maxm],cnt,nxt[maxm],cost[maxm],flow[maxm],pre[maxn],dis[maxn];
char s[maxn][maxn];
void addedge(int u,int v,int c,int f){
	pnt[cnt]=v;nxt[cnt]=head[u];cost[cnt]=c;flow[cnt]=f;head[u]=cnt++;
	pnt[cnt]=u;nxt[cnt]=head[v];cost[cnt]=-c;flow[cnt]=0;head[v]=cnt++;
}
bool spfa(){
	bool vis[maxn]={false};
	rep(i,st,end+1){
		pre[i]=-1;
		dis[i]=inf;
	}
	dis[st]=0;
	queue<int> q;
	vis[st]=true;
	q.push(st);
	while (!q.empty()){
		int u=q.front();
		q.pop();
		for (int i=head[u];i!=-1;i=nxt[i])
			if (flow[i]&&dis[u]+cost[i]<dis[pnt[i]]){
				dis[pnt[i]]=dis[u]+cost[i];
				pre[pnt[i]]=i;
				if (!vis[pnt[i]]){
					q.push(pnt[i]);
					vis[pnt[i]]=true;
				}
			}
		vis[u]=false;
	}
	return dis[end]!=inf;
}
void Build(){
	cnt=0;
	memset(pnt,0,sizeof pnt);
	memset(nxt,0,sizeof nxt);
	memset(cost,0,sizeof cost);
	memset(flow,0,sizeof flow);
	st=0,end=2*n*m+1;
	int p=2*n*m+2;
	memset(head,-1,sizeof head);
	addedge(st,p,0,k);
	rep(i,1,n*m){
		addedge(st,i,0,1);
		addedge(n*m+i,end,0,1);
		addedge(p,n*m+i,0,1);
	}
	rep(i,0,n-1)	rep(j,0,m-1){
		rep(k,j+1,m-1)
			if (s[i][j]==s[i][k])	addedge(i*m+j+1,n*m+i*m+k+1,-(s[i][j]-'0'-(k-j-1)),1);
			else addedge(i*m+j+1,n*m+i*m+k+1,k-j-1,1);
		rep(k,i+1,n-1)	
			if (s[i][j]==s[k][j])	addedge(i*m+j+1,n*m+k*m+j+1,-(s[i][j]-'0'-(k-i-1)),1);
			else addedge(i*m+j+1,n*m+k*m+j+1,k-i-1,1);
	}
}
void mincostflow(){
	int ans=0,sum=0;
	while (spfa()){
		int mini=inf;
		for (int i=pre[end];i!=-1;i=pre[pnt[i^1]])	mini=min(mini,flow[i]);
		for (int i=pre[end];i!=-1;i=pre[pnt[i^1]]){
			flow[i]-=mini;
			flow[i^1]+=mini;
		}
		sum+=mini;
		ans+=dis[end]*mini;
	}
	if (sum==n*m)	printf("%d\n",-ans);
	else printf("-1\n");
}
int main(){
	int T;
	scanf("%d",&T);
	rep(z,1,T){
		scanf("%d%d%d",&n,&m,&k);
		rep(i,0,n-1)	scanf("%s",s[i]);
		printf("Case %d : ",z);
		Build();
		mincostflow();
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值