构图方法:
加入超级源s和超级汇t,每个城市分成两个节点wi和ui,所有到达第i城市的航线都指向wi,所有从第i城市出发的航线都从ui指出,每个城市i都连接一条从wi指向ui的路径,容量设置为该城市指定的流量vi,费用设置为-INF(为了吸引流量,使得流量竟可能地流过城市i,这样可以保证指定的流量vi得到满足)。由于可以从任意城市出发,所以从s向每个城市的wi连接一条路径,容量设置为w,费用设置为0。同理每个城市都可以是某人旅行的终点,于是从每个城市的vi向超级汇t连接一条容量w费用为0的路径。城市之间的路径就很简单了,从ui指向wj,费用为costij,容量INF。
最后只要求流量为m的最小费用流即可。(记得加上每个城市减去的INF费用)
#include "stdafx.h"
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
const int MAX_V = 1000;
int const MIN_COST = -10000;
const int INF = (1 << 31) - 1;
int n, m;
int V; //实际顶点数
struct edge{
int to, cap, cost, rev;
};
vector<edge> G[MAX_V];
int dist[MAX_V]; //最短距离
int prevv[MAX_V], preve[MAX_V]; //最短路中的前驱结点和对应边
void add_edge(int from, int to, int cap, int cost)
{
edge e1 = { to, cap, cost, G[to].size() };
edge e2 = { from, 0, -cost, G[from].size() };
G[from].push_back(e1);
G[to].push_back(e2);
}
//求解从s到t流量为f的最小费用流
//如果不能在增广则返回-1
int min_cost_flow(int s, int t, int f)
{
int res = 0;
while (f > 0)
{
//利用bellman-ford算法求s到t的最短路
fill(dist, dist + MAX_V, INF);
dist[s] = 0;
bool update = true;
while (update)
{
update = false;
for (int v = 0; v < V; v++)
{
if (dist[v] == INF)
continue;
for (int i = 0; i < G[v].size(); i++)
{
edge &e = G[v][i];
if (e.cap>0 && dist[e.to]>dist[v] + e.cost)
{
dist[e.to] = dist[v] + e.cost;
prevv[e.to] = v;
preve[e.to] = i;
update = true;
}
}
}
}
if (dist[t] == INF)
return -1;
//沿着s到t的最短路增广
int d = f;
for (int v = t; v != s; v = prevv[v])
{
d = min(d, G[prevv[v]][preve[v]].cap);
}
f -= d;
res += d*dist[t];
for (int v = t; v != s; v = prevv[v])
{
edge &e = G[prevv[v]][preve[v]];
e.cap -= d;
G[v][e.rev].cap += d;
}
}
return res;
}
int _tmain(int argc, _TCHAR* argv[])
{
int a, fl = 0;;
scanf_s("%d%d", &n, &m);
for (int i = 0; i < n; i++)
{
scanf_s("%d", &a);
add_edge(i + 1, i + 1 + n, a, MIN_COST);//1~n为流入点wi,n+1~2n为流出点ui
fl += a;
}
for (int i = 1; i <= n - 1; i++)
{
for (int j = 1; j <= n - i; j++)
{
scanf_s("%d", &a);
if(a!=-1)
add_edge(i + n, i + j, INF, a);
}
}
for (int i = 1; i <= n; i++)
{
add_edge(0, i, m, 0); //0为超级源
add_edge(i + n, 2 * n + 1, m, 0); //2*n+1为超级汇
}
V = 2 * n + 2;
int ans = min_cost_flow(0, 2 * n + 1, m);
ans -= fl*MIN_COST;
printf_s("%d\n", ans);
return 0;
}