【CF 724E】Goods transportation(最小割+DP)
题目大意:
n个工厂,每个工厂有生产量
p
i
p_i
pi和最多销售量
s
i
s_i
si。小编号工厂可以往大编号工厂运送货物,每对工厂最多传送c单位的货物。传送顺序随意。
问最终所有工厂最多销售量。
可以想到最大流。源点与每个工厂一条边,流量 p i p_i pi,每个工厂和汇点一条边,流量 s i s_i si。每个小工厂往每个大工厂有一条边,流量 c c c。
跑完就可以得到结果了。
但是这题n 10^4,图是妥妥的建不出来。由长者们推出来的定理:最大流=最小割……
考虑将图分成两部分,集合A与集合B,A中有源点,B中有汇点,$A \cup B $=原图。
这样割就是 ∑ i ∈ A s i + ∑ j ∈ B p j + ∑ i < j , i ∈ A , j ∈ B c \sum\limits_{i \in A}s_i+\sum\limits_{j \in B}p_j+\sum\limits_{i<j,i \in A,j \in B}c i∈A∑si+j∈B∑pj+i<j,i∈A,j∈B∑c
画出图来比较好看。大体就是:
被叉掉的就是一个割,也就是刚才那一长串。
求最小割的dp数组的定义也很绝。
d p [ i ] [ j ] dp[i][j] dp[i][j]表示图中存在1~i工厂,其中j个工厂与源点连通的最小割。
枚举工厂1~n,加点求min就行了。要注意的是直接n^2内存开不下,要做成滚动数组。
代码如下:
#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <climits>
#include <ctime>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define fread(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
const double eps = 1e-8;
const int maxn = 11234;
LL p[maxn],s[maxn];
LL dp[2][maxn];
int main()
{
//fread("");
//fwrite("");
int n;
LL c;
scanf("%d%lld",&n,&c);
for(int i = 1; i <= n; ++i) scanf("%lld",&p[i]);
for(int i = 1; i <= n; ++i) scanf("%lld",&s[i]);
int pre = 1;
memset(dp,INF,sizeof(dp));
dp[pre][0] = 0;
for(int i = 1; i <= n; ++i)
{
pre ^= 1;
for(int j = 0; j <= i; ++j)
{
dp[pre][j] = dp[pre^1][j]+p[i]+j*c;
if(j) dp[pre][j] = min(dp[pre][j],dp[pre^1][j-1]+s[i]);
}
}
LL ans = dp[pre][0];
for(int i = 1; i <= n; ++i)
ans = min(ans,dp[pre][i]);
printf("%lld\n",ans);
return 0;
}