题意:曹操有一些船,这些船很多是连在一起的,现在让你派一些士兵去烧船,其中要求派出的士兵数最少(隐含意:由于一些船是连在一起的,所以就是一个连通块派出一个士兵)现在问你烧掉所有的船所需要的最少时间。
思路:先使用并查集将每个联通快,直接标记即可,然后遍历每个联通快里的节点,找出每个烧掉每个联通快所需要的最少时间,然后取所有联通快烧掉时所用时间最长的一个。
#include <bits/stdc++.h>
using namespace std;
const int maxn=1050;
int n;
int mat[maxn][maxn];
vector<int> v[maxn];
int a[maxn];
vector<int> sub[maxn];
int vis[maxn];
int id[maxn];
int t[maxn];
int inq[maxn];
const int inf=0x3f3f3f;
int dis[maxn];
int fa[maxn];
void Init()
{
for(int i=0; i<=n; i++)
{
v[i].clear();
fa[i]=i;
}
}
int Find_x(int x)
{
return fa[x]=(x==fa[x]?x:Find_x(fa[x]));
}
int spfa(int u)
{
queue<int> p;
p.push(u);
memset(inq,0,sizeof(inq));
inq[u]=1;
memset(dis,inf,sizeof(dis));
dis[u]=0;
while(!p.empty())
{
int s=p.front();
p.pop();
inq[s]=0;
for(int i=0; i<v[s].size(); i++)
{
int son=v[s][i];
if(dis[son]>dis[s]+mat[s][son])
{
dis[son]=dis[s]+mat[s][son];
if(!inq[son])
{
inq[son]=1;
p.push(son);
}
}
}
}
int ans=0;
for(int i=1; i<=n; i++)
{
if(dis[i]<inf)///很奇怪的地方,dis[i]无缘无故变得比inf还大
{
ans=max(dis[i],ans);
}
}
return ans;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
memset(t,inf,sizeof(t));
Init();
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
scanf("%d",&mat[i][j]);
if(mat[i][j]!=-1)
{
v[i].push_back(j);
int fx=Find_x(i);
int fy=Find_x(j);
fa[fx]=fy;///因为图是对称的,这样合并以后,每个联通快的里的fa都相同。
}
}
}
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
for(int i=1; i<=n; i++)
{
t[fa[i]]=min(spfa(i)+a[i],t[fa[i]]);
}
int ans=0;
for(int i=1; i<=n; i++)
ans=max(ans,t[fa[i]]);
cout<<ans<<endl;
}
}