题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5545
题目大意:有n个村庄,m个战场。对于村庄i,曹操可以支付c[i]*num元,使得这个村庄派遣num个士兵去战场x[i]为曹操作战,同时,这个村庄也会派遣num个士兵去战场y[i]为袁绍作战。
对于每个战场,有3种重要度(0,1,2)。
重要度为2表示该战场曹操的士兵数必须大于袁绍的士兵数;重要度为1表示该战场曹操的士兵数必须不 小于袁绍的士兵数;重要度为0则对两方士兵数无任何要求。
求曹操若想赢得战争,至少要支付多少钱。
思路:首先,从贪心的角度来看。最省的策略应是:重要度为2的战场,曹兵=袁兵+1;重要度为1的战场,曹兵=袁兵。
考虑从曹战场向袁战场连边。然后,对于某个重要度为2的战场A,我们给该战场曹兵数+1,则对应的边的另一头战场B袁兵数+1,为了满足要求,我们再给战场B的曹兵数+1,则B又经过某条边转移到战场C... ...直到转移到某个重要度为0的战场为止。此时,我们完成了对战场A的分配。所以大体思路就是枚举重要度为2的战场,进行如上的转移(即找一条到重要度为0的战场的最短路径),然后累加答案即可。
上面的思路其实是一个多源最短路问题,但是题目数据量太大,因此可以把边逆过来,初始化所有重要度为0的点为起点,求最短路。 然后累加所有起点到重要度为2的点的最短路即可。
#include<cstdio>
#include<cstring>
#include<string>
#include<cctype>
#include<iostream>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<queue>
#include<algorithm>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
using namespace std;
const int maxn = 1e5 + 10;
const ll INF = 1e13 + 10;
struct Edge {
int from, to, cost;
Edge(int from, int to, int cost) : from(from), to(to), cost(cost){}
};
vector<Edge> G[maxn];
int x[maxn], y[maxn], c[maxn], w[maxn], vis[maxn];
ll d[maxn];
struct HeapNode {
int d, u;
HeapNode(int d, int u) : d(d), u(u) {}
bool operator < (const HeapNode& rhs) const {
return d > rhs.d;
}
};
priority_queue<HeapNode> Q;
ll Dijkstra(int n) {
memset(vis, 0, sizeof vis);
for(int i = 1; i <= n; i++) {
if(w[i] == 0) {
Q.push(HeapNode(0, i));
d[i] = 0;
}
else d[i] = INF;
}
while(!Q.empty()) {
HeapNode x = Q.top(); Q.pop();
int u = x.u;
if(vis[u]) continue;
vis[u] = 1;
for(int i = 0; i < G[u].size(); i++) {
Edge& e = G[u][i];
if(d[e.to] > d[e.from] + e.cost) {
d[e.to] = d[e.from] + e.cost;
Q.push(HeapNode(d[e.to], e.to));
}
}
}
ll ans = 0;
for(int i = 1; i <= n; i++) if(w[i] == 2) {
if(d[i] == INF) return -1;
ans += d[i];
}
return ans;
}
int main() {
int T, kase = 0;
scanf("%d", &T);
while(T--) {
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++) G[i].clear();
for(int i = 1; i <= n; i++) scanf("%d", &x[i]);
for(int i = 1; i <= n; i++) scanf("%d", &y[i]);
for(int i = 1; i <= n; i++) scanf("%d", &c[i]);
for(int i = 1; i <= m; i++) scanf("%d", &w[i]);
for(int i = 1; i <= n; i++) G[y[i]].push_back(Edge(y[i], x[i], c[i]));
ll ans = Dijkstra(m);
printf("Case #%d: %lld\n", ++kase, ans);
}
return 0;
}