問題の説明:
東洞は繁栄している都市Cに住んでいます。しかし、この都市のほとんどの道路は荒廃しています。市長は市民の便宜のためにいくつかの道路を再建しようとしていたので、東洞を見つけ、東東が彼を助けることができることを望みました。
C市にはさらに重要な場所がn個あり、市長はこれらの場所が検討されることを望んでいます。これで、これらの場所のいくつかを接続するためにいくつかの道路を構築でき、各道路はそれらの2つを接続できます。また、C市を流れる河川があるため、これらの場所の一部にドックを建設することもでき、ドックを建設するすべての場所を河川水路で接続することができます。
東洞は、建設可能な各道路の費用、ドックを建設できる場所、ドックの建設費用など、建設が許可されている道路に関する情報を受け取りました。
市長は、東洞が新設された道路や河川を介してのみ、2つの場所が互いに到達できるようにすると同時に、コストを可能な限り低くする計画を立てることを望んでいます。
入力形式:入力
の最初の行には、2つの整数n、mが含まれています。これらは、それぞれC市の重要な場所の数と建設可能な道路の数を表しています。すべての場所には、1からnまで順番に番号が付けられています。
次のm行では、各行に3つの整数a、b、cがあります。これは、場所aから場所bへの道路をcのコストで構築できることを意味します。cが正の場合、建設に費用がかかることを意味します。cが負の場合、道路の建設(有料道路の建設など)後にお金を稼ぐことができることを意味します。
次の行には、n個の整数w_1、w_2、…、w_nが含まれています。w_iが正の数の場合、場所iに埠頭を建設するコストを意味し、w_iが-1の場合、場所iに埠頭を建設できないことを意味します。
入力により、少なくとも1つの方法が保証され、2つの場所が新しく建設された道路または川を介してのみ相互に到達できるようになります。
出力形式:
整数を含む1行を出力し、新しい道路またはドックを介してすべての場所を接続するための最小コストを示します 条件が満たされていてもお金を稼ぐことができる場合は、負の数を出力する必要があります。
入力例:
5 5
1 2 4
1 3 -1
2 3 3
2 4 5
4 5 10
-1 10 10 1 1
サンプル出力:
9
説明例:
場所4と5に2番目、3番目、4番目の道路とドックを建設するための総費用は9です。
データのスケールと規則データの
20%の場合、1 <= n <= 10、1 <= m <= 20、0 <= c <= 20、w_i <= 20、
データの50%の場合、1 <= n < = 100,1 <= m <= 1000、-50 <= c <= 50、w_i <= 50;
データの70%の場合、1 <= n <= 1000;
データの100%の場合、1 <= n <= 10000、1 <= m <= 100000、-1000 <= c <= 1000、-1 <= w_i <= 1000、w_i≠0。
サンプルの
概略図: 1-3-2-4道路建設、および4と5の間のドック。
質問のアイデア:
ドックを構築する場合と構築しない場合の2つの状況があります。道路自体が接続されている場合は、ドックを構築できます。道路が切断されている場合は、ドックを構築する必要があります。kruskal()アルゴリズムが使用されます。道路のコストが負の場合は、構築する必要があります。
ドックの場合、ドックの構築を犠牲にして、すべてのドックを0の番号が付いた頂点に接続できます。
クラスカルアルゴリズムc参照コード:
#include <stdio.h>
#include <stdlib.h>//qsort()函数头文件。
#define MAXN 10001//1<=n<=10000。
#define MAXM 100001//1<=m<=100000。
int n,m;
int parent[MAXN];
typedef struct{
int u,v;
int w;
}Road,*road;
Road a[MAXM+MAXN];
/*qsort()比较函数*/
int cmp(const void *a,const void *b)
{
road pa=(road)a;
road pb=(road)b;
int num1=pa->w;
int num2=pb->w;
return num1-num2;
}
/*并查集初始化操作*/
void init()
{
int i;
for(i=0;i<=n;i++)
parent[i]=i;
}
/*并查集寻找根节点操作*/
int find(int x)
{
if(x==parent[x])
return x;
else
return parent[x]=find(parent[x]);
}
/*kruskal算法*/
int kruskal(int m)
{
int sum=0,i;
for(i=0;i<m;i++)
{
int A=find(a[i].u);
int B=find(a[i].v);
if(A!=B||a[i].w<0)//能赚钱的边一定要加入。
{
parent[A]=B;
sum+=a[i].w;
}
}
return sum;
}
int main()
{
scanf("%d%d",&n,&m);
int i,j,cost;
for(i=0;i<m;i++)
scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
j=m;
for(i=1;i<=n;i++)
{
scanf("%d",&cost);
if(cost!=-1)
{
a[j].u=0;
a[j].v=i;
a[j++].w=cost;
}
}
init();
for(i=0;i<m;i++)
{
int A=find(a[i].u);
int B=find(a[i].v);
if(A!=B)
parent[A]=B;
}
for(i=2;i<=n;i++)
{
if(find(1)!=find(i))
break;
}
int temp1,temp2;
if(i==n+1)//如果不加码头就能连通,则分别求出加码头和不加码头的最小生成树的权值和,两者比较,取较小者。
{
init();
qsort(a,m,sizeof(a[0]),cmp);
temp1=kruskal(m);
init();
qsort(a,j,sizeof(a[0]),cmp);
temp2=kruskal(j);
if(temp1<temp2)
printf("%d",temp1);
else
printf("%d",temp2);
}
else//不加码头不能连通,则必须加上码头,求出此时最小生成树的权值即可。
{
init();
qsort(a,j,sizeof(a[0]),cmp);
printf("%d",kruskal(j));
}
return 0;
}
参照:
ポータル