在最大流的基础上,找到一个费用最小的。
都还不知道这个算法有没有名字。就是SPFA套一个求最小割。
借用OJ的一个比喻。就是从A到B,有三种路线选择,汽车,飞机,走路,假设走的路都是相同的,这时走路费用是最少的。
因此每次找一条最短路,对他求最小割(也就是最大流),然后将ans加上最大流x最短路。
(可以这样来理解。就是以本题为模型,最短路求的是单价之和,最小割求的是最大的数量)
求最大费用最大流,思路相同,但是要改两个地方,赋初值为负无穷,spfa中比较要反号。
要注意的地方:
1、求完最小费用最大流,不能用一开始的边的信息求最大费用最大流,因为残量被修改了。
我的解决方案是建两条相同的边。
2、求最大费用最大流的时侯除S以外的点要赋初值为负无穷。不能赋为零,因为这样一开始就无法对其他点松弛了。
3、找增广轨的时候一定要判断残量是否大于零,同样适用于这里的找最短路。
话说这道题也可以用KM来做。
题目没有给出数据范围,一开始就随便取了一个,没想到因为空间的缘故,导致超时了。把数据规模改小了之后就过了。我想可能是因为构造函数调用次数太多了吧。
#include <cstdio>
#include <queue>
#include <cstring>
const long maxn=10000;
long m;
long n;
long S;
long T;
bool vis[maxn];
long dist[maxn];
struct node
{
long i;
node* next;
};
struct bm
{
long u;
long v;
long cap;
long val;
bm* next;
bm* b;
};
bm* head[maxn];
bm* head2[maxn];
bm* pre[maxn];
long size;
std::queue<long> que;
const long inf = 0x7fff0000;
bool spfa()
{
while (!que.empty())que.pop();
memset(vis,0,sizeof(vis));
memset(pre,0,sizeof(pre));
memset(dist,0x70,sizeof(dist));
dist[S] = 0;
que.push(S);
vis[S] = true;
while (!que.empty())
{
long u = que.front();
que.pop();
vis[u] = false;
for (bm* vv=head[u]; vv; vv=vv->next)
{
long v = vv->v;
long cap = vv->cap;
long val = vv->val;
if (cap>0&&dist[u]+val<dist[v])
{
dist[v] = dist[u]+val;
pre[v] = vv;
if (!vis[v])
{
vis[v] = true;
que.push(v);
}
}
}
}
if (pre[T])return true;
return false;
}
bool spfa2()
{
while (!que.empty())que.pop();
memset(vis,0,sizeof(vis));
memset(pre,0,sizeof(pre));
memset(dist,0xfe,sizeof(dist));
dist[S] = 0;
que.push(S);
vis[S] = true;
while (!que.empty())
{
long u = que.front();
que.pop();
vis[u] = false;
for (bm* vv=head2[u]; vv; vv=vv->next)
{
long v = vv->v;
long cap = vv->cap;
long val = vv->val;
if (cap>0&&dist[u]+val>dist[v])
{
dist[v] = dist[u]+val;
pre[v] = vv;
if (!vis[v])
{
vis[v] = true;
que.push(v);
}
}
}
}
if (pre[T])return true;
return false;
}
void fee_maxflow()
{
long ans = 0;
long ans2 = 0;
long min;
while (1)
{
if (!spfa()) break;
//ans += dist[T];
min = inf;
bm* p = pre[T];
while (p)
{
if (p->cap<min)
min = p->cap;
p = pre[p->u];
}
ans += min * dist[T];
p = pre[T];
while (p)
{
p->cap -= min;
p->b->cap += min;
p = pre[p->u];
}
}
while (1)
{
if (!spfa2()) break;
//ans += dist[T];
min = inf;
bm* p = pre[T];
while (p)
{
if (p->cap<min)
min = p->cap;
p = pre[p->u];
}
ans2 += min * dist[T];
p = pre[T];
while (p)
{
p->cap -= min;
p->b->cap += min;
p = pre[p->u];
}
}
printf ("%ld\n",ans);
printf ("%ld\n",ans2);
}
void insert(long u,long v,long cap,long val)
{
bm* nn = new bm;
nn->u = u;
nn->v = v;
nn->cap = cap;
nn->val = val;
nn->next = head[u];
head[u] = nn;
nn = new bm;
nn->u = v;
nn->v = u;
nn->cap = 0;
nn->val = -val;
nn->next = head[v];
head[v] = nn;
head[u]->b = head[v];
head[v]->b = head[u];
}
void insert2(long u,long v,long cap,long val)
{
bm* nn = new bm;
nn->u = u;
nn->v = v;
nn->cap = cap;
nn->val = val;
nn->next = head2[u];
head2[u] = nn;
nn = new bm;
nn->u = v;
nn->v = u;
nn->cap = 0;
nn->val = -val;
nn->next = head2[v];
head2[v] = nn;
head2[u]->b = head2[v];
head2[v]->b = head2[u];
}
int main()
{
freopen("transport.in", "r", stdin);
freopen("transport.out", "w", stdout);
scanf("%ld%ld",&m,&n);
size = m+n+2;
S = size;
T = size - 1;
for (long i=n+1; i<n+m+1; i++)
{
long tmp = 0;
scanf("%ld",&tmp);
insert(S,i,tmp,0);
insert2(S,i,tmp,0);
}
for (long i=1; i<n+1; i++)
{
long tmp = 0;
scanf("%ld",&tmp);
insert(i,T,tmp,0);
insert2(i,T,tmp,0);
}
for (long i=n+1; i<n+m+1; i++)
for (long j=1; j<n+1; j++)
{
long tmp = 0;
scanf("%ld",&tmp);
insert(i,j,inf,tmp);
insert2(i,j,inf,tmp);
}
fee_maxflow();
return 0;
}