uva 1658Admiral

来源:互联网 发布:知乎怎么粘贴 编辑:程序博客网 时间:2024/04/26 20:34

原题:
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
W 1 and W 2 , there is at most one passage W 1 → W 2 . 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. a i (1 ≤ a i ≤ v), the starting-point of a passage, which is represented by a waypoint;
2. b i (1 ≤ b i ≤ v) and (a i ̸= b i ), the end-point of a passage, which is represented by a waypoint.
All passages are directed passages;
3. c i (1 ≤ c i ≤ 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 cannon-
balls 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

中文:
给你一个图,最多1000个节点,10000个边。边上有权值,现在让你从起点,到终点走两次,每次不能走同样的节点。现在问你两次权值和最小是多少?

#include<bits/stdc++.h>using namespace std;const int maxn=2000+5;struct Edge{    int from,to,cap,flow,cost;    Edge(int u,int v,int c,int f,int w):from(u),to(v),cap(c),flow(f),cost(w){}};struct MCMF{    int n,m;    vector<Edge> edges;    vector<int> G[maxn];    int inq[maxn];    int d[maxn];    int p[maxn];    int a[maxn];    void init(int n)    {        this->n=n;        for(int i=1;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,long long& cost)    {        for(int i=1;i<=n;i++)            d[i]=INT_MAX;        memset(inq,0,sizeof(inq));        d[s]=0;        inq[s]=1;        p[s]=0;        a[s]=INT_MAX;        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]==INT_MAX)            return false;        flow+=a[t];        cost+=(long long )d[t]*(long long)a[t];        for(int u=t;u!=s;u=edges[p[u]].from)        {            edges[p[u]].flow+=a[t];            edges[p[u]^1].flow-=a[t];        }        return true;    }    int MincostMaxflow(int s,int t,long long& cost)    {        int flow = 0;        cost=0;        while(BellmanFord(s,t,flow,cost));        return flow;    }};MCMF M;int main(){    ios::sync_with_stdio(false);    int n,m,f,t,c;    while(cin>>n>>m)    {        M.init(n*2);        for(int i=2;i<=n-1;i++)        {            M.AddEdge(i,i+n,1,0);        }        M.AddEdge(1,n+1,2,0);        M.AddEdge(n,n*2,2,0);        for(int j=1;j<=m;j++)        {            cin>>f>>t>>c;            M.AddEdge(f+n,t,1,c);        }        long long cost;        M.MincostMaxflow(1,n*2,cost);        cout<<cost<<endl;    }    return 0;}

解答:
类似 uva 10806,使用最小费用流问题解决。
但是 uva 10806是每次是不走相同的边,现在要去不走相同的点。
现在将点拆成两个节点,中间连接一条边,边上的流量容量设置为1,权值设置为0。
源点压入流量2,汇点最后的流入的流量2。

最后计算最小费用流即可。

原创粉丝点击