HDU 5545 The Battle of Guandu (最短路Dijkstra)

题目链接: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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值