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;
}



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