hdu 5545 The Battle of Guandu

26 篇文章 0 订阅

http://acm.hdu.edu.cn/showproblem.php?pid=5545
题意:
有T(30)组数据。
对于每组数据有n(1e5)个村庄,m(1e5)个战场
对于村庄i,曹操可以选择支付c[i]*num元,(0<=c[i]<=1e5)
使得这个村庄派出num个士兵,在战场x[i]为曹操作战,在战场y[i]为袁绍作战。(1<=x[i],y[i]<=m)
然后,我们有一个关键性的问题就是,如何保证题目的要求呢?
首先我们简化问题,只去考虑合法性。
对于所有战略价值为2的战场,如果从这个样的点ST出发,沿着图,能够达到任意一个战略价值为0的点。
那说明,这个战略价值为2的战场,可以使得其上的曹兵比袁兵多。
为什么能说明这个呢?
很显然,我们对于一条边,使得ST的曹兵+1,下一个点的袁兵+1。
但是这样,对于下一个点是有影响的。
如果下个点重要度为0,显然这样已经可以了。
如果下个点重要度为1,我们还要继续把这1个人转移出去,一直转移到重要度为0的点上即可。
如果下个点重要度为2,我们依然一定要把这1个人转移出去。而只要转移出去了,下个点实际上其实并没有受到影响。

枚举每个重要度为2的点,
对于每个这样的点,求它到重要度为0的点中距离最近的那个的距离。
然后这些距离求和即可。

但是这样是多源最短路。
然而我们发现,只要把边逆过来,初始化所有重要度为0的点为起点,都归为ZERO,求最短路。
然后累加所有重要度为2的点的到 ZERO的最短路即可。

这道题最短路我写的有问题,因此一直WRONG!

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int inf=0x7f7f7f7f;
int t,n,m;
const int maxn=1e5+10;
struct node{
    int to;ll cost;
}a[maxn];
vector<node>e[maxn];
queue<int>q;
int x[maxn],vis[maxn],y[maxn],c[maxn],w[maxn];
ll dis[maxn];
void init(){
    for(int i=0;i<maxn;i++){
        dis[i]=1e15;e[i].clear();vis[i]=0;
    }
    while(!q.empty()) q.pop();
}    
ll bfs(){
    while(!q.empty()){
        int now=q.front();q.pop();
        vis[now] = 0;//出队列就一定要消除标记,因为标记是用来判断是不是需要入队的,这个点有可能再被其他的点更新到,而此时它需要再次入队,去用新的值来更新后面的值!
        int size=e[now].size();
        for(int i=0;i<size;i++){
            int to=e[now][i].to;
        //    if(vis[to]) continue;
          //  vis[to]=1;//这处就太残了,dis是一直都需要更新的
            if(dis[to]>dis[now]+e[now][i].cost){
                dis[to]=dis[now]+(ll)e[now][i].cost;
                if(vis[to] == 0){//标记是判断是否需要入队的,已经入队了就不用再入队了!
                q.push(to);
                vis[to] = 1;
                }
            }
        }
    }ll sum=0;
    for(int i=1;i<=m;i++){
        if(w[i]==2){
            if(dis[i]==1e15) return -1;
            sum+=dis[i];
        }
    }
    return sum;
}
int main(){
    cin>>t;int count=1;
    while(t--){
        scanf("%d%d",&n,&m);
        init();
        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++){
            e[y[i]].push_back({x[i],c[i]});
            if(w[y[i]]==0) {q.push(y[i]);dis[y[i]]=0;vis[y[i]]=1;}
        }
        printf("Case #%d: ",count++);
        printf("%lld\n",bfs());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值