最小费用最大流MCMF 配题(HDU 6611)

最小费用最大流、最大费用最大流都是一样的。

其要求就是在最大流的基础上费用的最值

解决方案,寻找增广路的时候通过SPFA算法进行增广,如果要返回所需要的费用,就是在增广之后返回需要的权值即可。

配题:HDU 6611

题意:寻求 k 个子上升子序列(不重复),然后返回点权之和,使其最大。

思路:上升序列的问题可以通过MCMF来完成,建图思路如下:

  • 定义超级源点source,第二个超级远点sec_source,汇点sink。
  • 从source指向sec_source建一条费用为 0,容量为 k 的边。
  • 将数组总的每一个索引下的数字当作一个点,需要将点拆成边,也就是从 i 指向 n+i 建一条费用为点权,容量为 1的边。
  • 如果第 j(j > i)个元素的值大于等于第 i 个元素的值,从 n+i 指向 j 建一条费用为 0,容量为 1 的边。
  • 从sec_source向每一个点 i 连接一条费用为 0, 容量为 1 的边。
  • 从每一个点 n+i 向sink连一条费用为 0,容量为 1 的边。
  • 建图完毕#

无语地方:我用上面的方法写了之后,TLE,一晚上,然后搜CSDN发现,建图过程的第三步,设置一个上限,找到 100 个就不用再往后搜索了,然后我就试了一下,然后。。。TM Ac了。。。那个博客作者给的解释是“玄学”???

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<vector>
using namespace std;
const int maxn = 4005;
const int INF = 0x7fffffff;

struct EDGE
{
	int u, v;
	int fee;
	int flow;
	EDGE(int u, int v, int fee, int flow): u(u), v(v), fee(fee), flow(flow){}
};
vector<int>graph[maxn];
vector<EDGE>edge;
int a[maxn];
int source, sink;
int searchback_node[maxn], searchback_edge[maxn];
int dis[maxn];
bool vis[maxn];
int tot_fee;

void init(int n)
{
	for (int i = 1; i <= n; i++)
	{
		graph[i].clear();
	}
	edge.clear();
}

void add_edge(int u, int v, int fee, int flow)
{
	edge.push_back(EDGE(u, v, fee, flow));
	edge.push_back(EDGE(v, u, -fee, 0));
	int cnt = edge.size();
	graph[u].push_back(cnt - 2);
	graph[v].push_back(cnt - 1);
}

bool dinic_spfa(int& fees)
{
	queue<int>q;
	while (!q.empty()) q.pop();
	memset(searchback_node, -1, sizeof(searchback_node));
	memset(dis, -1, sizeof(dis));
	memset(vis, false, sizeof(vis));
	
	vis[source] = true;
	dis[source] = 0;
	q.push(source);
	
	while (!q.empty())
	{
		int cur_node = q.front();
		q.pop();
		vis[cur_node] = false;
		for (int i = 0; i < graph[cur_node].size(); i++)
		{
			int id = graph[cur_node][i];
			int next_node = edge[id].v;
			int fee = edge[id].fee;
			int flow = edge[id].flow;
			if (dis[next_node] < dis[cur_node] + fee && flow > 0)
			{
				dis[next_node] = dis[cur_node] + fee;
				searchback_node[next_node] = cur_node;
				searchback_edge[next_node] = id;
				if (!vis[next_node])
				{
					vis[next_node] = true;
					q.push(next_node);
				}
			}
		}
	}	
	if (dis[sink] == -1)
	{
		return false;
	}
	else 
	{
		fees += dis[sink];
		return true;
	}
}

int Min(int x, int y)
{
	if (x < y) return x;
	else return y;
}

int Dinic()
{
	int cut_flow;
	int fees = 0;
	while (dinic_spfa(fees))
	{
		cut_flow = INF;
		for (int cur_node = sink; cur_node != source; cur_node = searchback_node[cur_node])
		{	
			cut_flow = Min(cut_flow, edge[searchback_edge[cur_node]].flow);
		}	
//		cout<< "cut_flow="<< cut_flow<< endl;
		for (int cur_node = sink; cur_node != source; cur_node = searchback_node[cur_node])
		{
			int id = searchback_edge[cur_node];
			edge[id].flow -= cut_flow;
			edge[id^1].flow += cut_flow;
		}
	}
	return fees;
}

void show_node(int cur_node)
{
	for (int i = 0; i < graph[cur_node].size(); i++)
	{
		int id = graph[cur_node][i];
		int next_node = edge[id].v;
		int flow = edge[id].flow;
		int fee = edge[id].fee;
		cout<< cur_node<< "->"<< next_node<< " <"<< fee<< ","<< flow<< ">"<< endl;
	}
	cout<< endl;
}

int main()
{
	int T;
	cin>> T;
	while (T--)
	{
		int n, k;
		cin>> n>> k;
		for (int i = 1; i <= n; i++)
		{
			cin>> a[i];
		}
		source = 0;
		int sec_source = 2*n + 2;
		sink = 2*n + 1;
		init(2*n+2);
		
		add_edge(source, sec_source, 0, k);
		for (int i = 1; i <= n; i++)
		{
			add_edge(sec_source, i, 0, 1);
			add_edge(i, n+i, a[i], 1);
			for (int j = i + 1, up = 0; j <= n && up <= 100; j++)
			{
				if (a[j] >= a[i])
				{
					up++; 
					add_edge(i+n, j, 0, 1);
				}
			}
			add_edge(i+n, sink, 0, 1);
		}
		
		cout<< Dinic()<< endl; 
	}
	return 0;
}
发布了331 篇原创文章 · 获赞 135 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/Triple_WDF/article/details/103195828