POj-3723 Conscription-最大權森林
問題描述:
Windy has a country, and he wants to build an army to protect his country. He has picked up N girls and M boys and wants to collect them to be his soldiers. To collect a soldier without any privilege, he must pay 10000 RMB. There are some relationships between girls and boys and Windy can use these relationships to reduce his cost. If girl x and boy y have a relationship d and one of them has been collected, Windy can collect the other one with 10000-d RMB. Now given all the relationships between girls and boys, your assignment is to find the least amount of money Windy has to pay. Notice that only one relationship can be used when collecting one soldier.
AC程式碼:
struct edge
{
int u;
int v;
int w;
};
edge e[100010];
int n,m,r;
int boss[20010];
bool cmp(const edge &a,const edge &b)
{
return a.w < b.w;
}
int get_boss(int x)
{
if(boss[x] == x)
return x;
else
return boss[x] = get_boss(boss[x]);// TLE!不要忘記路徑壓縮
}
void merge(int x,int y)
{
int bx = get_boss(x);
int by = get_boss(y);
if(bx != by)
boss[by] = bx;
return;
}
bool query(int x,int y)
{
int bx = get_boss(x);
int by = get_boss(y);
return bx == by;
}
int kruskal()//最小生成森林
{
int ans = 0;
sort(e + 1,e + r + 1,cmp);//貪心排序
for(int i = 1; i <= r; ++i)
{
if(query(e[i].u,e[i].v) == false)
{
merge(e[i].u,e[i].v);
ans += e[i].w;
}
}
return ans;
}
int main()
{
int t;
cin >> t;
while(t--)
{
cin >> n >> m >> r;
for(int i = 1; i <= n + m; ++i)//初始化並查集
boss[i] = i;
for(int i = 1; i <= r; ++i)
{
int x,y,d;
scanf("%d%d%d",&x,&y,&d);
e[i].u = x + 1;//下標從1開始
e[i].v = y + n + 1;//頂點編號不能重合,下標從1開始
e[i].w = -d;//取相反數
}
cout << 10000 * (n + m) + kruskal() << endl;
}
return 0;
}
解決方法:
《挑戰》上的問法是:要求通過適當的徵募順序使得徵募所有人的所需費用最小
在徵募順序這個問法上算是難住我了,只有已經徵募的人的關係才能夠使用,沒毛病啊。
所需費用就是10000*(m+n) - 關係優惠
關係優惠需要求個最大值,關係優惠是若干邊權和,由於徵募某人時,某人只能動用和已經徵募的人之中一個人的關係,不能動用多個關係,所以這就是求最大生成樹。最大生成樹可以通過將邊權取反求最小生成樹來求得。
越看這道題越像生成樹,那麼徵募順序怎麼體現呢。可以將生成樹看做有序樹,根結點先徵募,葉子結點後徵募(生成樹本身是無序樹,將其認為是有序樹也是等價的),這樣就打消了我的疑慮,題目吻合到生成樹問題上來了。
聯想到圖中的序關係,容易想到拓撲排序,拓撲序存在的必要條件是圖中沒有環,否則會產生矛盾。這道題徵募順序還是有要求的,所以給出的圖必沒有環。
但是也未必保證生成的是一棵樹,於是在kruskal中省去cnt == n - 1的判斷。
對時間順序的先後作出等價的變換或是用模型良好地解釋會讓求解更順利地進行。
注意頂點的起始下標是1還是0,這會影響到並查集