hdu1285


题目大意;听说lcy帮大家预定了新马泰7日游,Wiskey真是高兴的夜不能寐啊,他想着得快点把这消息告诉大家,虽然他手上有所有人的联系方式,但是一个一个联系过去实在太耗时间和电话费了。他知道其他人也有一些别人的联系方式,这样他可以通知其他人,再让其他人帮忙通知一下别人。你能帮Wiskey计算出至少要通知多少人,至少得花多少电话费就能让所有人都被通知到吗?

Input
多组测试数组,以EOF结束。
第一行两个整数N和M(1<=N<=1000, 1<=M<=2000),表示人数和联系对数。
接下一行有N个整数,表示Wiskey联系第i个人的电话费用。
接着有M行,每行有两个整数X,Y,表示X能联系到Y,但是不表示Y也能联系X。
Output
输出最小联系人数和最小花费。
每个CASE输出答案一行。

//参考了别人的代码。。ya

思路:强连通+缩点...把图缩点成连通分量,找连通分量中入度为0的;再找入度为0 的连通分量中的最小费用。。因为入度为0 的话说明一定要联系其中的一个人。所以找费用最小的


#include <stdio.h>
#include <string.h>
#include <vector>
#include <stack>
using namespace std;
vector<int > p[1002],in[1003],belong[1003];//p[]出度,in【】入度belong[]记录连通分量的值
stack <int >q;
int low[1003],dfn[1003],v[1003],fei[1003],be[1003],mc[1003];//mc最小费用
int sum,now,n,m;
int min(int a,int b)
{
 return a>b?b:a;
}
void init()
{
 int i;
 sum=0;now=1;
 memset(low,0,sizeof(low));
 memset(dfn,0,sizeof(dfn));
 memset(v,0,sizeof(v));
 for(i=0;i<=n;i++)
 {mc[i]=10000000;}
 for(i=0;i<=n;i++)
 {p[i].clear();belong[i].clear();in[i].clear();}
 while (!q.empty())
  q.pop();
}
void tarjon( int i)
{
 int j,t;
 v[i]=1;
 dfn[i]=low[i]=now++;
 q.push(i);
 for(j=0;j<p[i].size();j++)
 {
  t=p[i][j];
  if(dfn[t]==0)
  {
   tarjon(t);
   low[i]=min(low[i],low[t]);
  }
  else if(v[t]==1)
   low[i]=min(low[i],dfn[t]);
 }
 if(dfn[i]==low[i])
 {
  sum++;
  while (!q.empty())
  {
   j=q.top();
   q.pop();
   v[j]=0;
   belong[sum].push_back(j);
   be[j]=sum;   //记录j属于哪个连通分量的
   if(mc[sum]>fei[j]) mc[sum]=fei[j];
   if(j==i) break;
  }
 }
}
void check()
{
 int i,j,k,ii,jj,cnt;
 int flag,money=0;
 cnt=sum;
 for(i=1;i<=cnt;i++)//枚举连通分量
 {
  flag=0;
  for(j=0;j<belong[i].size();j++)//第i个连通分量里存的数
  {
   k=belong[i][j];
   for(ii=0;ii<in[k].size();ii++)//找他的入度是否为0
   {
    jj=in[k][ii];
    if(be[jj]!=i)            //不为0,因为jj不属于改模块
    {
     flag=1;
     sum--;   // 有入度,所以该模块不需要联系,so--
     break;
    }
   }
   if(flag==1) break;
  }
  if(!flag) money+=mc[i];
 }
 printf("%d %d\n",sum,money);
}
void judge()
{
 int i;
 for(i=1;i<=n;i++)
  if(dfn[i]==0)
   tarjon(i);
 check();
}
int main()
{
  int i;
  int a,b;
 // freopen("D://in.txt","r",stdin);
  while (scanf("%d%d",&n,&m)!=EOF)
  {
   init();
   for(i=1;i<=n;i++)
    scanf("%d",&fei[i]);
   for(i=0;i<m;i++)
   {
    scanf("%d%d",&a,&b);
    p[a].push_back(b);
    in[b].push_back(a);
   }
   judge();
  }
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值