【最大费用最大流】贴纸与玩偶

输出格式:输出总愉悦值的最大值。


大致思路

又是这种“对应匹配关系”,很容易让人联想到建图。这明显是一个最大费用的题,所以毋庸置疑肯定也是想要最大流的

按照算法模板把spfa改成求(费用权)最长路即可。注意要修改两个地方:距离数组d的初始化要为-inf,用于更新的if语句<改成>。而且在算法模板里要注意,因为是while循环spfa,所以在spfa里要重新初始化visit数组和d数组和pre数组。

加深了对最小/大费用最大流的题的建图的理解——权值不是有两个吗,一个是c容量,一个是w费用权,那么结合题意,我们取的容量c一般是该物品的“数量”,取的费用权w一般是题中给的比如说时间啊价值啊什么的定义)”。这里的图中,左边排列点“贴纸”,右边排列点“玩偶”,左边从源点连线为贴纸数量,右边连线到汇点为玩偶数量,两边的费用权都为0,连接贴纸i和玩偶j的边中,容量可以设为inf(当然设为min(贴纸i数量,玩偶j数量)),费用权设为H(i,j)。


AC代码

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
 

const int MAX_N = 1000;
const int MAX_M = 10000;
const int inf = 0x3f3f3f3f;

struct edge {
    int v, c, w, next;  // v 表示边的另一个顶点,c 表示当前剩余容量,w 表示单位流量费用
} e[MAX_M];
int p[MAX_N], s, t, eid;  // s 表示源点,t 表示汇点,需要在进行 costflow 之前设置完毕
void init() {
    memset(p, -1, sizeof(p));
    eid = 0;
}
void insert(int u, int v, int c, int w) {
    e[eid].v = v;
    e[eid].c = c;
    e[eid].w = w;
    e[eid].next = p[u];
    p[u] = eid++;
}
void addedge(int u, int v, int c, int w) {
    insert(u, v, c, w);
    insert(v, u, 0, -w);
}
bool inq[MAX_N];
int d[MAX_N];  // 如果到顶点 i 的距离是 0x3f3f3f3f,则说明不存在源点到 i 的最短路
int pre[MAX_N];  // 最短路中连向当前顶点的边的编号
bool spfa() {  // 以源点 s 为起点计算单源最短路,如果不存在从 s 到 t 的路径则返回 false,否则返回 true
    memset(inq, 0, sizeof(inq));
    memset(d, -0x3f, sizeof(d));
    memset(pre, -1, sizeof(pre));
    d[s] = 0;
    inq[s] = true;
    queue<int> q;
    q.push(s);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        inq[u] = false;
        for (int i = p[u]; i != -1; i = e[i].next) {
            if (e[i].c) {
                int v = e[i].v;
                if (d[u] + e[i].w > d[v]) {
                    d[v] = d[u] + e[i].w;
                    pre[v] = i;
                    if (!inq[v]) {
                        q.push(v);
                        inq[v] = true;
                    }
                }
            }
        }
    }
    return pre[t] != -1;
}

int costflow() {  // 计算最小费用最大流
    int ret = 0;  // 累加和
    while(spfa()) {
        int flow = inf;
        for(int i = t; i != s; i = e[pre[i]^1].v) {
            flow = min(e[pre[i]].c, flow);  // 计算当前增广路上的最小流量
        }
        for(int i = t; i != s; i = e[pre[i]^1].v) {
            e[pre[i]].c -= flow;
            e[pre[i]^1].c += flow;
            ret += e[pre[i]].w * flow;
        }
    }
    return ret;
}
int tiezhi[MAX_N];
int wanou[MAX_N];
int main()
{
	init();
	int m,n;
	cin>>m>>n;
    s=0;
	t=m+n+1;
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&tiezhi[i]);
		addedge(s,i,tiezhi[i],0);	
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&wanou[i]);
		addedge(m+i,t,wanou[i],0);
	}
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			int h;
			scanf("%d",&h);
			addedge(i,m+j,0x3f3f3f3f,h);
		}
	}
	cout<<costflow();
	return 0;
	
}
主函数最开始别忘了要先init()啊!!不然运行超时一脸懵逼!!!

猜你喜欢

转载自blog.csdn.net/m0_38033475/article/details/80259336