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