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));
    }
}
posted @ 2020-04-07 08:59  灰灰烟影  阅读(206)  评论(0编辑  收藏  举报