您的位置:首页 > 其它

zoj 2676 Network Wars 【0-1分数规划 + 最小割】 【吃一堑长一智】

2015-09-02 00:25 316 查看
Network Wars

Time Limit: 5 Seconds Memory Limit: 32768 KB Special Judge

Network of Byteland consists of n servers, connected by m optical cables. Each cable connects two servers and can transmit data in both directions. Two servers of the
network are especially important --- they are connected to global world network and president palace network respectively.
The server connected to the president palace network has number 1, and the server connected to the global world network has number n.
Recently the company Max Traffic has decided to take control over some cables so that it could see what data is transmitted by the president palace users. Of course they want
to control such set of cables, that it is impossible to download any data from the global network to the president palace without transmitting it over at least one of the cables from the set.
To put its plans into practice the company needs to buy corresponding cables from their current owners. Each cable has some cost. Since the company's main business is not spying, but
providing internet connection to home users, its management wants to make the operation a good investment. So it wants to buy such a set of cables, that cables mean cost} is minimal possible.
That is, if the company buys k cables of the total cost c, it wants to minimize the value of c/k.

Input

There are several test cases in the input. The first line of each case contains n and m (2
<= n <= 100 , 1 <= m <= 400 ).
Next m lines describe cables~--- each cable is described with three integer numbers: servers
it connects and the cost of the cable. Cost of each cable is positive and does not exceed 107.

Any two servers are connected by at most one cable. No cable connects a server to itself. The network is guaranteed to be connected, it is possible to transmit data from any server to
any other one.
There is an empty line between each cases.

Output

First output k ---
the number of cables to buy. After that output the cables to buy themselves. Cables are numbered starting from one in order they are given in the input file. There should an empty line between each cases.

Example

InputOutput
6 8
1 2 3
1 3 3
2 4 2
2 5 2
3 4 2
3 5 2
5 6 3
4 6 3

4
3 4 5 6

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

3
1 2 3

Source: Andrew Stankevich's Contest #8

我是一个花了4小时错了2页然后AC这道题的人!!!

Segmentation Fault 只因为我mark数组开小了。。。

找了那么长长时间,真是醉了。吃一堑长一智 /(ㄒoㄒ)/~~

题意:给你一个N个点和M条边的图,已经给出每条边的边权。现在让你找出阻断1和N的一个边割集,要求边权平均值最小。最后答案输出任意一组边割集里面边的编号。



分析:题目需要求一个割,使得c / k最小。其中c为割集中所有边的权值之和,k为割集里面的边数。我们可以将问题转化为求f(x) = sigma(x * ci) / sigma(x
* 1)的最小值,(x == 0 || x == 1)

设o = sigma(xi * ci) / sigma(xi * 1) -> sigma(xi * ci) - sigma(xi * 1) * o = 0。

构造函数g(x) = min(
x * sigma(ci - o) )。

这样可以对于图中每一条边进行重赋权:c' = c - o。而g(x)就是在这个重新构造的图中求出一个s-t的最小割集。

再提一下g(x)函数,因为它的目的是取最小的加权和,所以在赋权时,对于负权的边则不用加入新图。可以明确的是这样的边一定属于边割集,而对于其它边则需要加入新图,然后跑一次最大流求出最小割。

特别的,在进行二分查找的过程中。

设o'是f(x)的最小值,则对于当前查找解mid

如果 t(mid) = 0 那么 ans = mid

如果 t(mid) < 0 那么 ans > mid

如果 t(mid) > 0 那么 ans < mid

今天大脑太累了,写个大概的解析。详细请看:点我看原版论文

写代码时注意精度!!!

累死了,该休息了。。。 (~﹃~)~zZ

AC代码:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <cmath>
#include <algorithm>
#define MAXN 110
#define MAXM 2000
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
struct Edge
{
    int from, to;
    double cap, flow;
    int ID, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int dist[MAXN], cur[MAXN];
bool vis[MAXN];
int N, M;
struct rec
{
    int from, to;
    double val;
};
rec num[MAXM];
double Max;
void input()
{
    Max = 0;
    for(int i = 1; i <= M; i++)
        scanf("%d%d%lf", &num[i].from, &num[i].to, &num[i].val), Max = max(Max, num[i].val);
}
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, double w, int id)
{
    Edge E1 = {u, v, w, 0, id, head[u]};
    edge[edgenum] = E1;
    head[u] = edgenum++;
    Edge E2 = {v, u, 0, 0, id, head[v]};
    edge[edgenum] = E2;
    head[v] = edgenum++;
}
double ans;//记录总值
void getMap(double o)
{
    ans = 0;//初始化
    for(int i = 1; i <= M; i++)
    {
        double t = num[i].val - o;
        if(t > eps)
            addEdge(num[i].from, num[i].to, t, i), addEdge(num[i].to, num[i].from, t, i);
        else
            ans += t;//该边就是割边
    }
}
bool BFS(int s, int t)
{
    queue<int> Q;
    memset(dist, -1, sizeof(dist));
    memset(vis, false, sizeof(vis));
    dist[s] = 0;
    vis[s] = true;
    Q.push(s);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge E = edge[i];
            if(!vis[E.to] && E.cap - E.flow > eps)
            {
                dist[E.to] = dist[u] + 1;
                if(E.to == t) return true;
                vis[E.to] = true;
                Q.push(E.to);
            }
        }
    }
    return false;
}
double DFS(int x, double a, int t)
{
    if(x == t || fabs(a) < eps) return a;//精度
    double flow = 0, f;
    for(int &i = cur[x]; i != -1; i = edge[i].next)
    {
        Edge &E = edge[i];
        if(dist[E.to] == dist[x] + 1 && (f = DFS(E.to, min(a, E.cap-E.flow), t)) > eps)
        {
            edge[i].flow += f;
            edge[i^1].flow -= f;
            flow += f;
            a -= f;
            if(fabs(a) < eps) break;//精度
        }
    }
    return flow;
}
double Maxflow(int s, int t)
{
    double flow = 0;
    while(BFS(s, t))
    {
        memcpy(cur, head, sizeof(head));
        flow += DFS(s, Max, t);
    }
    return flow;
}
void find_S(int u)//找源点能到的点集 S
{
    vis[u] = true;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        Edge E = edge[i];
        if(vis[E.to]) continue;
        if(E.cap - E.flow > eps)
            find_S(E.to);
    }
}
bool mark[MAXM];//标记该边是否是 选中最小割的边
int sum;//记录最小割里面 的边数
void solve()
{
    double l = 0, r = Max, mid;
    while(r - l >= eps)
    {
        mid = (l + r) / 2;
        init();
        getMap(mid);//每次根据mid值 重建图 
        ans += Maxflow(1, N);//求一次最小割
        if(ans > eps)
            l = mid;
        else
            r = mid;
    }
    memset(vis, false, sizeof(vis));
    find_S(1);//找源点能到 的点集S集
    sum = 0;//总边数
    memset(mark, false, sizeof(mark));
    for(int i = 1; i <= M; i++)
    {
        if((vis[num[i].from] && !vis[num[i].to]) || (!vis[num[i].from] && vis[num[i].to]) || num[i].val <= mid)
            sum++, mark[i] = true;
    }
    printf("%d\n", sum);
    int used = 0;
    for(int i = 1; i <= M; i++)
    {
        if(mark[i])
        {
            if(used) printf(" ");
            printf("%d", i);
            used++;
        }
    }
    printf("\n");
}
int main()
{
    int k = 0;
    while(scanf("%d%d", &N, &M) != EOF)
    {
        if(k)
            printf("\n");
        k++;
        input();
        solve();
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: