Dragons UVALive - 7018(二分 + 并查集)

题目链接:https://vjudge.net/problem/UVALive-7018

题意:n个城市之间有m条路。共有K条龙,第Ki条龙住在Ci城市,初始有Si个头,只要他活着(头的数目不为0)每分钟会长出Ni个头。现要雇佣x名猎人去杀龙,每分钟每个猎人有两种选择:(1)在当前城市砍去龙的一个头;(2)沿着路去往相邻城市。求x的最小值。

思路:二分判断mid是否是最小的可行解。由于城市间共有m条路,有些城市可能是不可达的,这样n个城市可能变成若干个联通快,对每个连通块用二分求解出最小的可行解,所有连通块的最小可行解求和即可。可用并查集将城市根据是否联通分成若干连通块。训练赛时我给的思路学弟写的代码,思路错了两次坑他WA了两次,尴尬啊。

代码如下:

#include<iostream>
#include<iomanip>
#include<sstream>
#include<string>
#include<algorithm>
#include<vector>
#include<list>
#include<stack>
#include<map>
#include<set>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>


using namespace std;


typedef long long ll;
typedef long double ld;
typedef pair<int,int> pii;


#define rep(a,b,c) for(int (a)=(b);(a)<=(c);(a)++)
#define drep(a,b,c) for(int (a)=(c);(a)>=(b);(a)--)


const int inf = 0x3f3f3f3f;
const int maxn = 1000+10;


int n,m,k;


int meida[310];
int da[310];
int fa[310];
vector<int>v[310];
vector<pair<int,int> >vv[310];
int id[310];
int geshu;
int vis[310];


bool check(int mid,int x)//判断第x个联通块雇佣mid个猎人是否可行
{
        int cun=mid;
        int sum=0;
        bool ok = false;
        int sz=v[x].size();
        for(int j=0;j<sz;j++)
        {
            int t=v[x][j];
            if(!vis[t]) continue;
            else
            {
                int ssz=vv[t].size();
                for(int k=0;k<ssz;k++)
                {
                    if(vv[t][k].second<mid) continue;//若增长速度小于猎人总数,一定可以杀死
                    else sum+=vv[t][k].first;//若增长速度大于猎人总数,则初始龙头总数之和必须小于猎人总数才行
                }
            }
        }
        return mid>=sum;
}
int finds(int x)
{
    return x==fa[x]?x:(fa[x]=finds(fa[x]));
}
int main()
{
    while(scanf("%d%d%d",&n,&m,&k)==3)
    {
        if(n==0&&m==0&&k==0) break;
        for(int i=0;i<=n;i++)
        {
            v[i].clear();
            vv[i].clear();
        }
        geshu=0;
        memset(id,-1,sizeof(id));
        memset(vis,0,sizeof(vis));
        int a,b;
        for(int i=0;i<=n;i++) fa[i]=i;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&a,&b);
            int aa=finds(a);
            int bb=finds(b);
            if(aa!=bb) fa[aa]=bb;
        }
        for(int i=1;i<=n;i++)//并查集划分连通块,同一连通块内城市根节点相同
        {
            fa[i]=finds(fa[i]);
        }
        for(int i=1;i<=n;i++)
        {
            if(id[fa[i]]==-1) {id[fa[i]]=geshu++;v[geshu-1].push_back(i);}//为每个连通块编号并将城市放入对应连通块集合中
            else v[id[fa[i]]].push_back(i);
        }
        int city,head,inc;
        for(int i=0;i<k;i++)
        {
            scanf("%d%d%d",&city,&head,&inc);
            vv[city].push_back(make_pair(head,inc));//每个城市可能有若干条龙
            vis[city]=1;//标记该城市是否有龙
        }
        int sum=0;
        for(int i=0;i<geshu;i++)//对每个连通块二分查找其最小可行解
        {
            int l=0,res,mid,r=inf;
            while(l<=r)
            {
                mid=(l+r)/2;
                if(check(mid,i)) {res=mid;r=mid-1;}
                else l=mid+1;
            }
            sum+=res;//最小可行解求和
        }
        printf("%d\n",sum);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值