洛谷 P2747 [USACO5.4]周游加拿大Canada Tour 解题报告

P2747 [USACO5.4]周游加拿大Canada Tour

题目描述

你赢得了一场航空公司举办的比赛,奖品是一张加拿大环游机票。旅行在这家航空公司开放的最西边的城市开始,然后一直自西向东旅行,直到你到达最东边的城市,再由东向西返回,直到你回到开始的城市。除了旅行开始的城市之外,每个城市只能访问一次,因为开始的城市必定要被访问两次(在旅行的开始和结束)。

当然不允许使用其他公司的航线或者用其他的交通工具。

给出这个航空公司开放的城市的列表,和两两城市之间的直达航线列表。找出能够访问尽可能多的城市的路线,这条路线必须满足上述条件,也就是从列表中的第一个城市开始旅行,访问到列表中最后一个城市之后再返回第一个城市。

输入输出格式

输入格式:

第 1 行: 航空公司开放的城市数 (N) 和将要列出的直达航线的数量 (V)(N) 是一个不大于 100 的正整数。(V) 是任意的正整数。

第 2..(N)+1 行: 每行包括一个航空公司开放的城市名称。城市名称按照自西向东排列。不会出现两个城市在同一条经线上的情况。每个城市的名称都 是一个字符串,最多15字节,由拉丁字母表上的字母组成;城市名称中没有空格。

(N+2)..N+2+V-1$ 行: 每行包括两个城市名称(由上面列表中的城市名称组成),用一个空格分开。这样就表示两个城市之间的直达双程航线。

输出格式:

Line 1: 按照最佳路线访问的不同城市的数量 M。如果无法找到路线,输出 1。


没想到DP怎么做,于是费用流暴力

发现回来的路其实等价于去的路

建图
1.拆点,费1流1
2.1号和n号点多一条费0流1的点,表示跑两次
3.按原图连边,费0流1


Code:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <map>
using namespace std;
const int N=502;
const int M=30000;
int head[N],to[M],Next[M],edge[M],cost[M],cnt=1;
void add(int u,int v,int w,int c)
{
    to[++cnt]=v;edge[cnt]=w;cost[cnt]=c;Next[cnt]=head[u];head[u]=cnt;
    to[++cnt]=u;edge[cnt]=0;cost[cnt]=-c;Next[cnt]=head[v];head[v]=cnt;
}
map <string,int > ma;
int n,m,s,t;
void init()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        string name;
        cin>>name;
        ma[name]=i;
        add(i,i+n,1,1);
    }
    s=1,t=n<<1;
    add(s,s+n,1,0);
    add(t-n,t,1,0);
    for(int i=1;i<=m;i++)
    {
        string u,v;
        cin>>u>>v;
        if(ma[u]>ma[v])
            swap(u,v);
        add(ma[u]+n,ma[v],1,0);
    }
}
int dis[N],pre[N],used[N];
bool spfa()
{
    queue <int > q;
    memset(dis,-0x3f,sizeof(dis));
    int inf=dis[s];
    dis[s]=0;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        used[u]=0;
        for(int i=head[u];i;i=Next[i])
        {
            int v=to[i],c=cost[i],w=edge[i];
            if(w&&dis[v]<dis[u]+c)
            {
                pre[v]=i;
                dis[v]=dis[u]+c;
                if(!used[v])
                {
                    used[v]=1;
                    q.push(v);
                }
            }
        }
    }
    return dis[t]!=inf;
}
void work()
{
    int maxcost=0,maxflow=0;
    while(spfa())
    {
        maxcost+=dis[t];
        maxflow++;
        int now=t;
        while(pre[now])
        {
            edge[pre[now]]-=1;
            edge[pre[now]^1]+=1;
            now=to[pre[now]^1];
        }
    }
    if(maxflow!=2) printf("1
");
    else printf("%d
",maxcost);
}
int main()
{
    init();
    work();
    return 0;
}

2018.8.2

原文地址:https://www.cnblogs.com/butterflydew/p/9409382.html