最小费用最大流、最大费用最大流都是一样的。
其要求就是在最大流的基础上费用的最值
解决方案,寻找增广路的时候通过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;
}