Time Limit: 4000MS | Memory Limit: 65536K | |
Total Submissions: 15010 | Accepted: 5154 |
Description
Input
Output
Sample Input
1 3 3 1 1 1 0 1 1 1 2 2 1 0 1 1 2 3 1 1 1 2 1 1 1 1 1 3 2 20 0 0 0
Sample Output
4-1
题意:有N个店家、M个仓库以及K种物品。接下来N行每行K个数,表示每个店家对每一种物品的需求。后面跟着M行,表示每个仓库所存储的每种物品的数目。
最后给出K个N*M的矩阵。对于第k个矩阵,它的第i行第j列表示——第j个仓库运送第k种物品到第i个店家的费用。现在问你能否满足所有店家的需求,若可以输出最小的花费,反之输出-1。
╮(╯▽╰)╭,一开始太任性了,把K种物品一起建边,然后跑一次MCMF求解最后答案,结果TLE5次。
最后把K种物品分开求解就AC了,跑了325ms。。。
思路:对每一种物品单独求解,累加结果即可。因为运输一种物品的最小费用是独立的,并且只要有一种物品不能满足所有店家的需求,就可以说明不存在一种可行方案。
建图:设置超级源点source,超级汇点sink。 这里只说明对第k种物品建边。
1,source到所有仓库建边,容量为该仓库所存储的第k种物品的数目,费用为0;
2,所有店家到sink建边,容量为店家对第k种物品的需求;
3,所有仓库到店家建边,容量为INF,因为我们可以任意选择通过该边的流量。至于费用(题目已给出);
声明,所有店家对第k种物品的需求就是超级源点传入的总流量,需要记录下这个值来判断是否满流。
最后跑一次MCMF,看是否满流即满足所有店家的需求。若可以累加结果,反之说明不可能实现(只要有一种物品不满足所有店家的需求,就失败)。
当然为了节约时间,我们就可以记录对每个物品的总需求和总存储量,若有一个物品的总需求大于总存储量,就可以说明不存在可行方案。
AC代码:
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> #define MAXN 200 #define MAXM 10000+10 #define INF 0x3f3f3f3f using namespace std; struct Edge { int from, to, cap, flow, cost, next; }; Edge edge[MAXM]; int head[MAXN], edgenum; int dist[MAXN], pre[MAXN]; bool vis[MAXN]; int source, sink;//超级源点 超级汇点 int N, M, K;//N个店主 M个仓库 K种食品 int need[60][60];//need[i][j] 第i个店主对第j种物品的需求 int have[60][60];//have[i][j] 第i个仓库里第j种物品的存货 int Sneed[60];//Sneed[i] 第i种物品的总需求 int Shave[60];//Shave[i] 第i种物品的总存货 int used[60][60];//在第k矩阵里面 存储i仓库到j店主的花费 bool flag;//判断能否满足所有人需求 void init() { edgenum = 0; memset(head, -1, sizeof(head)); } void addEdge(int u, int v, int w, int c) { Edge E1 = {u, v, w, 0, c, head[u]}; edge[edgenum] = E1; head[u] = edgenum++; Edge E2 = {v, u, 0, 0, -c, head[v]}; edge[edgenum] = E2; head[v] = edgenum++; } bool SPFA(int s, int t) { queue<int> Q; memset(dist, INF, sizeof(dist)); memset(vis, false, sizeof(vis)); memset(pre, -1, sizeof(pre)); dist[s] = 0; vis[s] = true; Q.push(s); while(!Q.empty()) { int u = Q.front(); Q.pop(); vis[u] = false; for(int i = head[u]; i != -1; i = edge[i].next) { Edge E = edge[i]; if(dist[E.to] > dist[u] + E.cost && E.cap > E.flow) { dist[E.to] = dist[u] + E.cost; pre[E.to] = i; if(!vis[E.to]) { vis[E.to] = true; Q.push(E.to); } } } } return pre[t] != -1; } void MCMF(int s, int t, int &cost, int &flow) { cost = flow = 0; while(SPFA(s, t)) { int Min = INF; for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) { Edge E = edge[i]; Min = min(Min, E.cap-E.flow); } for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) { edge[i].flow += Min; edge[i^1].flow -= Min; cost += edge[i].cost * Min; } flow += Min; } } void solve() { memset(Sneed, 0, sizeof(Sneed)); memset(Shave, 0, sizeof(Shave)); for(int i = 1; i <= N; i++)//店主需求 { for(int j = 1; j <= K; j++)//对每个物品的需求 { scanf("%d", &need[i][j]); Sneed[j] += need[i][j]; } } for(int i = 1; i <= M; i++)//每个仓库 { for(int j = 1; j <= K; j++)//每个物品 存货 { scanf("%d", &have[i][j]); Shave[j] += have[i][j]; } } flag = true; for(int i = 1; i <= K; i++) { if(Shave[i] < Sneed[i])//存货 少于需求 { flag = false; break; } } int ans = 0;//最后结果 int cost, flow; for(int k = 1; k <= K; k++)//K个矩阵 对K种物品依次求解 因为每个解都是独立的 { for(int i = 1; i <= N; i++) { for(int j = 1; j <= M; j++) scanf("%d", &used[i][j]);//第j个仓库运送第k种物品给第i个店主所需费用 } if(!flag) continue;//已经判断出 不能满足需求 init(); source = 0, sink = N+M+1; for(int i = 1; i <= N; i++)//店主 向超级汇点建边 addEdge(i, sink, need[i][k], 0); for(int i = 1; i <= M; i++)//超级源点 向仓库建边 addEdge(source, i+N, have[i][k], 0); for(int i = 1; i <= M; i++) { for(int j = 1; j <= N; j++) addEdge(i+N, j, INF, used[j][i]);//仓库 向 每个店主建边 } MCMF(source, sink, cost, flow); if(flow != Sneed[k])//总流量不等于 第k种物品的总需求 flag = false;//不满足 else ans += cost;//累加 每次的结果 } if(flag) printf("%d\n", ans); else printf("-1\n"); } int main() { while(scanf("%d%d%d", &N, &M, &K), N||M||K) { solve(); } return 0; }