hdu 5383 Yu-Gi-Oh! (费用流)
前言
最小费用最大流是指:满足最大流的情况下,让费用最小。
最小费用流:仅要求费用最小,通常情况下有费用为负的边权(如果费用全为正,那么可以让流量为0,费用也就是0),可以使用最小费用最大流的算法求解,只不过终止条件变为“从原点到汇点的费用为正”
最小费用最大流算法的原本终止条件为“从原点到汇点的容量为0”
题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=5383
思路
设置超级源点S,超级汇点T
S向0集怪兽建边,容量为1,费用为怪兽战斗力;
1集怪兽向T建边,容量为1,费用为怪兽战斗力;
0集怪兽向1集怪兽建边,容量为1,费用为合成怪兽最大战斗力-两个怪兽战斗力之和(前提是正数);
S向1集怪兽建边,容量为1,费用为0,表示该怪兽不与0集任何怪兽结合;
0集怪兽向T建边,容量为1,费用为0,表示该怪兽不与1集任何怪兽结合。
然后跑最大费用流,注意这里不用最大流,从原点到汇点的费用为负就停止。
#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 350;
const int M = 2e5+10;
struct edge
{
int to,val,cos,next;
}e[M];
int head[N],tot=-1;
int pre[N],dis[N],path[N];
bool vis[N];
struct node
{
int op,lev,atk;
}q[N];
int ma[N][N];
int n,m;
void init()
{
memset(head,-1,sizeof(head)),tot=-1;
memset(ma,0,sizeof(ma));
memset(path,0,sizeof(path));
}
void add(int u,int v,int w,int c)
{
e[++tot].to=v,e[tot].val=w,e[tot].cos=c;
e[tot].next=head[u],head[u]=tot;
e[++tot].to=u,e[tot].val=0,e[tot].cos=-c;
e[tot].next=head[v],head[v]=tot;
}
bool spfa(int s,int t)
{
memset(pre,-1,sizeof(pre));
memset(dis,-inf,sizeof(dis));
memset(vis,false,sizeof(vis));
queue<int>q;
dis[s]=0;
vis[s]=true;
q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();
vis[u]=false;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(e[i].val>0&&dis[u]+e[i].cos>dis[v])
{
dis[v]=dis[u]+e[i].cos;
pre[v]=u;
path[v]=i;
if(!vis[v])
{
vis[v]=true;
q.push(v);
}
}
}
}
if(pre[t]==-1)return false;
return true;
}
int mcmf(int s,int t)
{
int cost=0,flow=0;
while(spfa(s,t))
{
if(dis[t]<0)break; ///!!!!不需要满流,从原点到汇点的费用为负就停止
int f=inf;
for(int i=t;i!=s;i=pre[i])
f=min(e[path[i]].val,f);
flow+=f;
cost+=f*dis[t];
for(int i=t;i!=s;i=pre[i])
{
e[path[i]].val-=f;
e[path[i]^1].val+=f;
}
}
return cost;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
init();
scanf("%d%d",&n,&m);
int s=0,t=n+1;
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&q[i].op,&q[i].lev,&q[i].atk);
if(q[i].op==0)add(s,i,1,q[i].atk),add(i,t,1,0);
else add(i,t,1,q[i].atk),add(s,i,1,0);
}
int lev,atk,limit,a,b;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&lev,&atk,&limit);
if(limit==0)
{
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
{
if(j==k)continue;
if(q[j].op==q[k].op||q[j].lev+q[k].lev!=lev)continue;
if(q[j].op==0)ma[j][k]=max(ma[j][k],atk);
else ma[k][j]=max(ma[k][j],atk);
}
}
else if(limit==1)
{
scanf("%d",&a);
for(int j=1;j<=n;j++)
{
if(j==a)continue;
if(q[a].op==q[j].op||q[a].lev+q[j].lev!=lev)continue;
if(q[a].op==0)ma[a][j]=max(ma[a][j],atk);
else ma[j][a]=max(ma[j][a],atk);
}
}
else
{
scanf("%d%d",&a,&b);
if(q[a].op==q[b].op||q[a].lev+q[b].lev!=lev)continue;
if(q[a].op==0)ma[a][b]=max(ma[a][b],atk);
else ma[b][a]=max(ma[b][a],atk);
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(ma[i][j]>q[i].atk+q[j].atk)
add(i,j,1,ma[i][j]-q[i].atk-q[j].atk);
printf("%d\n",mcmf(s,t));
}
}