ACdream P1223 Roads

Roads

Special JudgeTime Limit: 2000/1000MS (Java/Others)Memory Limit: 128000/64000KB (Java/Others)

Problem Description

      The kingdom of Farland has N cities connected by M roads. Some roads are paved with stones, others are just country roads. Since paving the road is quite expensive, the roads to be paved were chosen in such a way that for any two cities there is exactly one way to get from one city to another passing only the stoned roads.

      The kingdom has a very strong bureaucracy so each road has its own ordinal number ranging from 1 to M: the stoned roads have numbers from 1 to N - 1 and other roads have numbers from N to M. Each road requires some money for support, i-th road requires ci coins per year to keep it intact. Recently the king has decided to save some money and keep financing only some roads. Since he wants his people to be able to get from any city to any other, he decided to keep supporting some roads in such a way, that there is still a path between any two cities.

      It might seem to you that keeping the stoned roads would be the good idea, however the king did not think so. Since he did not like to travel, he did not know the difference between traveling by a stoned road and travelling by a muddy road. Thus he ordered you to bring him the costs of maintaining the roads so that he could order his wizard to choose the roads to keep in such a way that the total cost of maintaining them would be minimal.

      Being the minister of communications of Farland, you want to help your people to keep the stoned roads. To do this you want to fake the costs of maintaining the roads in your report to the king. That is, you want to provide for each road the fake cost of its maintaining di in such a way, that stoned roads form the set of roads the king would keep. However, to lower the chance of being caught, you want the sum |c1-d1| + |c2-d2| + ... + |cm-dm| to be as small as possible.

      You know that the king's wizard is not a complete fool, so if there is the way to choose the minimal set of roads to be the set of the stoned roads, he would do it, so ties are allowed.

Input

      The first line of the input file contains N and M (2 ≤ N ≤ 60, N - 1 ≤ M ≤ 400). Next M lines contain three integer numbers ai, bi and ci each - the numbers of the cities the road connects (1 ≤ ai ≤ N, 1 ≤ bi ≤ N, ai != bi) and the cost of maintaining it (1 ≤ ci ≤ 10 000).

Output

      Output M lines - for each road output di that should be reported to be its maintainance cost so that the king would choose first N - 1 roads to be the roads to keep and the sum |c1-d1| + |c2-d2| + ... + |cm-dm| is minimal possible.

Sample Input

4 5
4 1 7
2 1 5
3 4 4
4 2 5
1 3 1

Sample Output

4
5
4
5
4

Source

Andrew Stankevich Contest 2

俄羅斯多校集訓中的一道題,很好地把KM算法原理隱藏在了題意中,題意是有n個點,m(m>=n)條路,m條路中前n-1條路保證是顆生成樹,國王只會修這幾條路,稱爲石路,我們要做的是在全部道路中修改最少,並保證前n-1條路是最小生成樹。看似和最小生成樹有關,事實上卻是一點關係也沒有,可以想裏面的條件總共有這些,在從n開始到m這段非最小生成樹中的線段,必會和1到n-1中的兩段線組成一個圈,並且這兩段線的長度均要小於這個線段。我們要求的是修改最小,設ci,cj爲石路和泥路,ci-li<=cj+lj li和lj均爲正數,li+lj>=ci-cj,並且共有一系列的不等式,這裏把ci和cj看成是不變的,那麼li的總和必是由一系列ci-cj組成的,這裏我們跑個以ci-cj爲費用的最大費用最大流就可以了,爲什麼是最大費用不是最小費用呢?因爲前面說過得保證前n-1條路是最小生成樹,既然每個條件都要滿足,答案就一定只會是最大費用,最小費用的一定沒有滿足所有條件。

但是此題並非費用流。。。

因爲費用流僅能算出最小的改變量,或許能有辦法算出方案,想必也是不好敲的,而KM算法就不一樣了,KM那種沒有機會也要創造機會的算法,恰好拿準了此題要輸出方案的考點,若你沒有很好地瞭解KM算法的精髓,那這題可能就走遠了。

代碼:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<string>
#include<cstring>
#include<algorithm>
#include<fstream>
#include<queue>
#include<stack> 
#include<vector>
#include<cmath>
#include<iomanip>
#define rep(i,n) for(i=1;i<=n;i++)
#define MM(a,t) memset(a,t,sizeof(a))
#define INF 1e9
typedef long long ll;
#define mod 1000000007
using namespace std;
struct edge{
	int s,e,v;
}eg[420];
int n,m,w[420][420],ind[420][420],lx[420],ly[420],l[420],slack[420];
bool ux[420],uy[420];
bool find(int x)
{
    ux[x]=1;
    for (int i=1;i<=m;i++)
    {
        if (!uy[i])
        {
            int t=lx[x]+ly[i]-w[x][i];
            if (t==0)
            {
                uy[i]=1;
                if (!l[i] || find(l[i]))
                {
                    l[i]=x;
                    return 1;
                }
            }
            else if (slack[i]>t) slack[i]=t;
        }
    }
    return 0;
}
void km()
{
    MM(lx,-0x3f);
    MM(ly,0);
    MM(l,0);
    for (int i=1;i<=m;i++)
        for (int j=1;j<=m;j++)
            if (w[i][j]>lx[i]) lx[i]=w[i][j];

    for (int j=1;j<=m;j++)
    {
        MM(slack,0x3f);
        while (1)
        {
            MM(ux,0);
            MM(uy,0);
            if (find(j)) break;
            int d=0x3f3f3f3f;
            for (int i=1;i<=m;i++)
                if (!uy[i] && slack[i]<d) d=slack[i];
            for (int i=1;i<=m;i++)
                if (ux[i]) lx[i]-=d;
            for (int i=1;i<=m;i++)
            {
                if (uy[i]) ly[i]+=d;
                else slack[i]-=d;
            }
        }
    }
}
bool dfs(int pre,int u,int v,int id)
{
    if (u==v) return 1;
    for (int i=1;i<=n;i++)
    {
        if (i==pre || !ind[u][i]) continue;
        if(dfs(u,i,v,id)){
          w[ind[u][i]][id]=eg[ind[u][i]].v-eg[id].v;
          //cout<<index[u][i]<<' '<<id<<' '<<w[index[u][i]][id]<<endl;
          return 1;	
        }
    }
    return 0;
}//找圈 
int main()
{
	int i,j;

    while(scanf("%d%d",&n,&m)!=EOF){
   	  MM(ind,0);
      rep(i,m){
        int s,e,v;
		scanf("%d%d%d",&s,&e,&v);
		eg[i].s=s; eg[i].e=e; eg[i].v=v;
		if(i<n) ind[s][e]=ind[e][s]=i;	
      }
      MM(w,0);
      for (i=n;i<=m;i++)
        dfs(0,eg[i].s,eg[i].e,i);
      km();
      rep(i,n-1) printf("%d\n",eg[i].v-lx[i]);
      for(i=n;i<=m;i++) printf("%d\n",eg[i].v+ly[i]);    	
    }
	
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章