uva 1658 Admiral - 拆点+最小费用流

uva 1658 Admiral

Michiel Adriaenszoon de Ruyter is the most famous admiral in Dutch history and is well known for his role in the Anglo-Dutch Wars of the 17th century. De Ruyter personally commanded a flagship and issued commands to allied warships during naval battles. In De Ruyter’s time, graph theory had just been invented and the admiral used it to his great advantage in planning his naval battles. Waypoints at sea are represented by vertices, and possible passages from one waypoint to another are represented as directed edges. Given any two waypoints W1 and W2 , there is at most one passage W1 → W2 . Each directed edge is marked with the number of cannonballs that need to be fired in order to safely move a ship along that edge, sinking the enemy ships encountered along the way. One of De Ruyter’s most successful tactics was the De Ruyter Manoeuvre. Here, two warships start at the same waypoint, and split up and fight their way through the enemy fleet, joining up again at a destination waypoint. The manoeuvre prescribes that the two warships take disjunct routes, meaning that they must not visit the same waypoint (other than the start and end-points), or use the same passage during the battle. Being Dutch, Admiral De Ruyter did not like to waste money; in 17th century naval warfare, this meant firing as few expensive cannonballs as possible.
Figure 1: A particular instance of De Ruyter’s tactic, visualised as a graph. Two ships (‘red’ and ‘blue’) move from a shared starting point (1) to a shared endpoint (6). The red ship’s route is 1 → 3 → 6 (firing 33 canonballs along the way); the blue ship’s route is 1 → 2 → 5 → 4 → 6 (firing 53 canonballs along the way). In total, 86 canonballs are fired during the manoeuvre. Except for the start- and end-point, no vertices or edges are visited by both ships.
Input
For each test case, the input consists of: • A line containing two integers v (3 ≤ v ≤ 1000) and e (3 ≤ e ≤ 10000), the number of waypoints and passages, respectively. • Then, e lines follow: for each passage, a line containing three integers: 1. ai (1 ≤ ai ≤ v), the starting-point of a passage, which is represented by a waypoint; 2. bi (1 ≤ bi ≤ v) and (ai ̸= bi), the end-point of a passage, which is represented by a waypoint. All passages are directed passages; 3. ci (1 ≤ ci ≤ 100), the number of cannonballs that are fired when travelling along this passage. The starting waypoint is 1 and the destination waypoint is v. There are always at least two disjunct routes from waypoint 1 to waypoint v.
Output
For each test case, the output consists of a single positive integer: the smallest possible sum of cannonballs fired by both ships when reaching the destination waypoint.
Sample Input
6 11

1 2 23

1 3 12

1 4 99

2 5 17

2 6 73

3 5 3

3 6 21

4 6 8

5 2 33

5 4 5

6 5 20
Sample Output
86

题目大意:

大概就是军舰分两组从起点到终点,条件是费用最小,而且两个队伍还不能都经过某个点,即,每个点只能被经过一次。

为什么不是最短路?哇我也解释不清楚,但是可以想一下,假如我们先跑一边最短路,然后把经过的点都删了, 再跑一边最短路,得到的两个答案可能大于结果啊!!

res1+res2 >=ans。反正就是解决不了啦。这个题要借助网络流,每个边只能走一遍,那么容量cap = 1就可以做到。那么每个点只能走一遍怎么办?

答案是: 再多加一个辅助点

将u拆成u和u',u~u'容量为1,费用为0,这样就能保证每个点只用一次啦!对不对!是不是这么回事!

对于原点和终点的处理,可以看先看一个比这个相对简单的题 poj 2135。看完肯定就能明白了,源点终点可以不用拆分,并且容量是2。

再就是编号处理的问题了, 这里是编号从0~n-1 又因为源点和终点不需要拆分,所以n~n-3 依次表示 编号为1~n-2的点拆出来的点


代码

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>

using namespace std;

const int INF = 0x3f3f3f3f;
const int maxn = 10000+10;

struct edge
{
    int to,cap,cost,rev;
};
vector <edge> G[maxn];
long long dist[maxn];
int prevv[maxn],preve[maxn];
int V;
void add_edge(int from,int to,int cap,int cost)
{
    G[from].push_back((edge){to,cap,cost,G[to].size()});
    G[to].push_back((edge){from,0,-cost,G[from].size()-1});
}

long long min_cost_flow(int s,int t,int f)
{
    long long res = 0;
    while(f>0)
    {
        memset(dist,INF,sizeof(dist));
        dist[s] = 0;
        bool update = true;
        while(update)
        {
            update = false;
            for(int v=0;v<=2*V-3;++v)///这里范围记得改
            {
                if(dist[v]==INF) continue;
                for(int i=0;i<G[v].size();++i)
                {
                    edge &e=G[v][i];
                    if(e.cap>0 && dist[e.to]>dist[v]+e.cost)
                    {
                        dist[e.to] = dist[v]+e.cost;
                        prevv[e.to] = v;
                        preve[e.to] = i;
                        update = true;
                    }
                }
            }
        }
        if(dist[t] == INF)
        {
            return -1;
        }

        int d = f;
        for(int v=t;v!=s;v=prevv[v])
        {
            d = min(d,G[prevv[v]][preve[v]].cap);
        }
        f-=d;
        res += d*dist[t];
        for(int v=t;v!=s;v=prevv[v])
        {
            edge &e = G[prevv[v]][preve[v]];
            e.cap -= d;
            G[v][e.rev].cap += d;
        }
    }
    return res;
}

int m;

void init(){
    memset(preve,0,sizeof(preve));
    memset(prevv,0,sizeof(prevv));
    for(int i = 0 ;i<maxn;++i){
        G[i].clear();
    }
}

int main()
{
    int a,b,c;
    while(scanf("%d%d",&V,&m)!=EOF){

        init();///初始化

        int s=0,t=V-1;///起点是0,终点是v-1;
        ///u,v ∈ [0,V-1]
        ///u',v' ∈ [V,2V-3]   u = 1 对应 u' = V;

        for(int i = 1; i <= V-2; ++i){
            add_edge(i,V+i-1,1,0);
        }
        for(int i=0;i<m;++i)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(a == 1){/// 起点
                add_edge(0,b-1,1,c);
            }
            add_edge(V+a-1-1,b-1,1,c);
        }
        printf("%lld\n",min_cost_flow(s,t,2));
    }


    return 0;
}
/*
6 11
1 2 23
1 3 12
1 4 99
2 5 17
2 6 73
3 5 3
3 6 21
4 6 8
5 2 33
5 4 5
6 5 20
*/
/*
6 6
1 2 23
1 3 12
2 5 17
3 6 21
4 6 8
5 4 5
*/


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值