uva 12092 - Paint the Roads(最小费用最大流)

In a country there are n cities connected by m one way roads. You can paint any of these roads. To paint a road it costs d unit of money where d is the length of that road. Your task is to paint some of the roads so that the painted roads can be partitioned into some disjoint cycles such that every vertex appears in exactly k of these disjoint cycles. But you have to minimize the costs of painting these roads.
Input

First line of the input contains T the number of test case. Then following lines contains T Test cases.

Each case starts with a line containing 3 integers n (1 ≤ n ≤ 40), m (1 ≤ m ≤ 2000) and k (1 ≤ k and 1≤k*n≤100). Next m lines contain description of m roads. Each line contains three integers f, t (0 ≤ f, t <n and f ≠ t) and d (0 ≤ d < 100). That means there is a road of d length from city f to city t. You can assume that there will be at most one road in one direction between two cities.

Output

For each test case output contains 1 integer denoting the minimum unit of money needed to paint roads. In the case it is impossible to paint the roads maintaining the constraints output -1.

Sample Input Output for Sample Input

4

4 8 1

0 1 1

1 0 2

2 3 1

3 2 2

0 2 5

2 0 6

1 3 5

3 1 6

4 8 1

0 1 1

1 0 10

2 3 10

3 2 1

0 2 10

2 0 1

1 3 1

3 1 10

4 8 2

0 1 1

1 0 2

2 3 1

3 2 2

0 2 5

2 0 6

1 3 5

3 1 6

3 4 1

0 1 5

1 0 6

0 2 7

2 0 8

 

6

4

28

-1


Problem setter: Abdullah al Mahmud, Special Thanks: Derek Kisman

 

还记得在匹配中做过的那道吗?同样的给一个带权有向图,找若干个圈,使得每个节点恰好属于一个圈。
这道题题意解析后其实就是使每个节点恰好属于k个圈。
那道题我们拆点后用最优匹配可以做,因为只要求每个点入度与出度为1,而这里要k,简单的最优匹配显然是不能满足了,
所以要用网络流的特性,加上流量的限制后就可以解决这个所谓的多重匹配问题了。
源点和汇点连的边容量为k,二分图中的边容量为1.
最后判断如果最大流量为n*k,则有解,否则无解

 

#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;

const int maxn = 202 + 10;
const int INF = 1000000000;

struct Edge {
  int from, to, cap, flow, cost;
};

struct MCMF {
  int n, m, s, t;
  vector<Edge> edges;
  vector<int> G[maxn];
  int inq[maxn];         // 是否在队列中
  int d[maxn];           // Bellman-Ford
  int p[maxn];           // 上一条弧
  int a[maxn];           // 可改进量

  void init(int n) {
    this->n = n;
    for(int i = 0; i < n; i++) G[i].clear();
    edges.clear();
  }

  void AddEdge(int from, int to, int cap, int cost) {
    edges.push_back((Edge){from, to, cap, 0, cost});
    edges.push_back((Edge){to, from, 0, 0, -cost});
    m = edges.size();
    G[from].push_back(m-2);
    G[to].push_back(m-1);
  }

  bool BellmanFord(int s, int t, int &flow,int &cost) {
    for(int i = 0; i < n; i++) d[i] = INF;
    memset(inq, 0, sizeof(inq));
    d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF;

    queue<int> Q;
    Q.push(s);
    while(!Q.empty()) {
      int u = Q.front(); Q.pop();
      inq[u] = 0;
      for(int i = 0; i < G[u].size(); i++) {
        Edge& e = edges[G[u][i]];
        if(e.cap > e.flow && d[e.to] > d[u] + e.cost) {
          d[e.to] = d[u] + e.cost;
          p[e.to] = G[u][i];
          a[e.to] = min(a[u], e.cap - e.flow);
          if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; }
        }
      }
    }
    if(d[t] == INF) return false;//s-t不连通,失败退出
    flow += a[t];
    //printf("d=%d a=%d\n",d[t],a[t]);
    cost += d[t] * a[t];
    int u = t;
    while(u != s) {
      edges[p[u]].flow += a[t];
      edges[p[u]^1].flow -= a[t];
      u = edges[p[u]].from;
    }
    return true;
  }

  // 需要保证初始网络中没有负权圈
  int Mincost(int& can,int s, int t) {
    int flow = 0,cost = 0;
    while(BellmanFord(s, t,flow, cost));
    can = flow;
    return cost;
  }

};

MCMF g;

int main(){
    int t,n,m,k;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d",&n,&m,&k);
        g.init(2*n+2);
        int sorce = 2*n,sink = 2*n+1;
        for(int i = 0;i < n;i++){
            g.AddEdge(sorce,i,k,0);
            g.AddEdge(i+n,sink,k,0);
        }
        for(int i = 0;i < m;i++){
            int from,to,dis;
            scanf("%d%d%d",&from,&to,&dis);
            g.AddEdge(from,to+n,1,dis);
        }
        int can;
        int ans = g.Mincost(can,sorce,sink);
        if(can == n*k)  printf("%d\n",ans);
        else    printf("-1\n");
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值