hdoj 5545 The Battle of Guandu 【差分约束系统 求解多源最短路】



The Battle of Guandu

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 53    Accepted Submission(s): 37


Problem Description
In the year of 200, two generals whose names are Cao Cao and Shao Yuan are fighting in Guandu. The battle of Guandu was a great battle and the two armies were fighting at  M  different battlefields whose numbers were 1 to  M . There were also  N  villages nearby numbered from 1 to  N . Cao Cao could train some warriors from those villages to strengthen his military. For village  i , Cao Cao could only call for some number of warriors join the battlefield  xi . However, Shao Yuan's power was extremely strong at that time. So in order to protect themselves, village  i  would also send equal number of warriors to battlefield  yi  and join the Yuan Shao's Army. If Cao Cao had called for one warrior from village  i , he would have to pay  ci  units of money for the village. There was no need for Cao Cao to pay for the warriors who would join Shao Yuan's army. At the beginning, there were no warriors of both sides in every battlefield.

As one of greatest strategist at that time, Cao Cao was considering how to beat Shao Yuan. As we can image, the battlefields would have different level of importance wi . Some of the battlefields with  wi=2  were very important, so Cao Cao had to guarantee that in these battlefields, the number of his warriors was greater than Shao Yuan's. And some of the battlefields with  wi=1  were not as important as before, so Cao Cao had to make sure that the number of his warriors was greater or equal to Shao Yuan's. The other battlefields with  wi=0  had no importance, so there were no restriction about the number of warriors in those battlefields. Now, given such conditions, could you help Cao Cao find the least number of money he had to pay to win the battlefield?
 

Input
The first line of the input gives the number of test cases,  T(1T30) T  test cases follow.

Each test case begins with two integers  N  and  M(1N,M105)  in one line.

The second line contains  N  integers separated by blanks. The  ith  integer  xi(1xiM)  means Cao Cao could call for warriors from village  i  to battlefield  xi .

The third line also contains  N  integers separated by blanks. The  ith  integer  yi(1yiM)  means if Cao Cao called some number of warriors from village  i , there would be the same number of warriors join Shao Yuan's army and fight in battlefield  yi .

The next line contains  N  integers separated by blanks. The  ith  integer  ci(0ci105)  means the number of money Cao Cao had to pay for each warrior from this village.

The last line contains  M  integers separated by blanks. The  ith  number  wi(wi0,1,2)  means the importance level of  ith  battlefield.
 

Output
For each test case, output one line containing  Case #x: y, where  x  is the test case number (starting from 1) and  y  is the least amount of money that Cao Cao had to pay for all the warriors to win the battle. If he couldn't win,  y=1 .
 

Sample Input
      
      
2 2 3 2 3 1 1 1 1 0 1 2 1 1 1 1 1 2
 

Sample Output
      
      
Case #1: 1 Case #2: -1
 


题意:在一场战争中,有m(<=100000)个战场和n(<=100000)个村庄,每个战场有一个重要度,重要度为0表示在这个战场己方输赢无所谓,重要度为1表示己方不能输,重要度为2表示己方必须胜出,己方获得战争的最终胜利当且仅当己方在每个战场的战果均不违背其重要度,每个战场输赢的判据只有人数,人多的一方胜出,若两方人数相同则打平,对于第i个村庄,每花费c[i]的代价能够征用一个人派到己方x[i]战场,同时有一个人会跑到敌方y[i]战场,问己方能否获得战争的最终胜利,若能,求出最小代价。


想到了费用流,没想到会超时。偷了大牛的题解。。。


题解:
考虑每个战场的净人数(己方人数-对方人数),那么相当于第i个村庄花费c[i]的代价使得y[i]战场净人数-1,x[i]战场净人数+1,相当于转移了1个人过来。建立如下费用流模型,源向重要度为0的战场连容量INF费用0的弧,重要度为2的战场向汇连容量1费用0的弧,对于第i个村庄,战场y[i]向x[i]连容量INF费用c[i]的弧。如果满流,说明每个重要度为2的战场净人数>0,并且每个重要度为1的战场由于出入流平衡,净人数=0,于是能获胜。但是直接跑费用流是会TLE的,考虑每一次增广都是找一条从源到汇最短路,并且每次增广流量限制总为1,连向汇的费用总为0,因此可以从源出发跑一次单源最短路得到每次增广的费用,复杂度O((n+m)log(n+m))。


注意:SPFA求解最短路时,会超int。

AC代码:


#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>
#define INF 0x3f3f3f3f3f
#define eps 1e-8
#define MAXN (100000+10)
#define MAXM (50000000)
#define Ri(a) scanf("%d", &a)
#define Rl(a) scanf("%lld", &a)
#define Rf(a) scanf("%lf", &a)
#define Rs(a) scanf("%s", a)
#define Pi(a) printf("%d\n", (a))
#define Pf(a) printf("%lf\n", (a))
#define Pl(a) printf("%lld\n", (a))
#define Ps(a) printf("%s\n", (a))
#define W(a) while(a--)
#define CLR(a, b) memset(a, (b), sizeof(a))
#define MOD 1000000007
#define LL long long
#define lson o<<1, l, mid
#define rson o<<1|1, mid+1, r
#define ll o<<1
#define rr o<<1|1
using namespace std;
struct Edge{
    int from, to;
    LL val; int next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
void init()
{
    edgenum = 0;
    CLR(head, -1);
}
void addEdge(int u, int v, LL w)
{
    Edge E = {u, v, w, head[u]};
    edge[edgenum] = E;
    head[u] = edgenum++;
}
int n, m;
int x[MAXN], y[MAXN];
LL c[MAXN]; int w[MAXN];
void getMap()
{
    init(); Ri(n); Ri(m);
    for(int i = 1; i <= n; i++)
        Ri(x[i]);
    for(int i = 1; i <= n; i++)
        Ri(y[i]);
    for(int i = 1; i <= n; i++)
        Rl(c[i]);
    for(int i = 1; i <= m; i++)
        Ri(w[i]);
    for(int i = 1; i <= n; i++)
        if(w[x[i]])
            addEdge(y[i], x[i], c[i]);
}
bool vis[MAXN];
LL dist[MAXN];
int kcase = 1;
void solve()
{
    getMap(); CLR(vis, false); //CLR(dist, INF);
    queue<int> Q;
    for(int i = 1; i <= m; i++)
    {
        if(w[i] == 0)
        {
            Q.push(i);
            dist[i] = 0;
            vis[i] = true;
        }
        else
            dist[i] = INF;
    }
    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.val)
            {
                dist[E.to] = dist[u] + E.val;
                if(!vis[E.to])
                {
                    vis[E.to] = true;
                    Q.push(E.to);
                }
            }
        }
    }
    LL ans = 0;
    bool flag = true;
    for(int i = 1; i <= m; i++)
    {
        if(w[i] == 2)
        {
            if(dist[i] == INF)
            {
                flag = false;
                break;
            }
            else
                ans += dist[i];
        }
    }
    if(flag)
        printf("Case #%d: %lld\n", kcase++, ans);
    else
        printf("Case #%d: -1\n", kcase++);
}
int main()
{
    int t; Ri(t);
    W(t){
        solve();
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值