[USACO5.4]周游加拿大&[网络流24题]航空路线问题

原题链接:https://www.luogu.org/problemnew/show/P2747

据说是IOI 1993?不过既然这道题没有奶牛,所以应该不是USACO原创题。

题意简述:给出n个城市,再给出m条双向边,要求求一条路径,能够从1号城市走到n号城市,再从n号城市返回1号城市,除了1号城市之外,其他城市都只能经过一次。

比较有意思的是关于字符串的处理,普遍都使用了STL,然而作为已经写过AC自动机而且STL非常菜的我,选择了trie树,在end标记上记录城市对应的编号。每次加边的时候查询这条边连接了哪两个城市,然后加到邻接矩阵里即可。

至于DP部分,完全可以转化为从1号城市出发的两条走到n的路径,设f[i][j]为第一条路径走到i,另一条走到j

转移方程:if(e[j][k])f[i][j]=max(f[i][j],f[i][k]+1);

当然,也可以像“最长k可重区间集问题”一样,转化为一个费用流解决。

PS:目前此题拿了luogu的rank3

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct tire
{
    int vis[33];
    int end;
}tr[2005];
char s[55],t[55];
int n,m,cnt,tot;
int e[105][105],f[105][105];
void build()
{
    int len=strlen(s);
    int tx=0;
    for(int i=0;i<len;i++)
    {
        int z;
        if(s[i]<='Z') z=s[i]-'A';
        else z=s[i]-'a';
        if(tr[tx].vis[z]==0) tr[tx].vis[z]=++cnt;
        tx=tr[tx].vis[z];
    }
    tr[tx].end=++tot;
}
int find(char a[])
{
    int len=strlen(a);
    int tx=0;
    for(int i=0;i<len;i++)
    {
        int z;
        if(a[i]<='Z') z=a[i]-'A';
        else z=a[i]-'a';
        tx=tr[tx].vis[z];
    }
    return tr[tx].end;
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s);
        build();
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%s %s",s,t);
        int l=find(s);
        int r=find(t);
//        printf("%d %d\n",l,r);
        e[l][r]=e[r][l]=1;
    }
    memset(f,-63,sizeof(f));
    f[1][1]=1;
    for(int i=1;i<n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            for(int k=1;k<j;k++)
            {
                if(e[j][k])
                {
                    f[i][j]=max(f[i][j],f[i][k]+1);
                    f[j][i]=f[i][j];
                }
            }
        }
    }
    int ans=1;
    for(int i=1;i<=n;i++)
    {
        if(e[i][n]) ans=max(ans,f[i][n]);
    }
    printf("%d",ans);
    return 0;
}

还有一道这道题的改版,其实是网络流24题里的航空路线问题

 

题目链接:https://www.luogu.org/problemnew/show/P2770

大概就是增加了输出方案的要求,所以要使用网络流。

将每个点看做有流量限制为1,使用最大费用最大流,记录最大流即可。

PS:此题luogu rank2,tires树大法好,比map不知道高到哪里去了

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=(1<<30)-1;
void read(int &y)
{
    y=0;char x=getchar();
    while(x<'0'||x>'9') x=getchar();
    while(x>='0'&&x<='9')
    {
        y=y*10+x-'0';
        x=getchar();
    }
}
int n,m,tot,num,t;
char s[105][105],st[105];
int l,r,sum,cnt=1;
int q[205],ans;
struct tire
{
    int vis[75],end;
}tr[2005];
int getnum(char x)
{
    if(x>='A'&&x<='Z') return x-'A';//0-25
    if(x>='a'&&x<='z') return x-'a'+26;//26-51
    if(x>='0'&&x<='9') return x-'0'+52;//51-61;
}
void build(char a[])
{
    int len=strlen(a),x=0;
    for(int i=0;i<len;i++)
    {
        int tx=getnum(a[i]);
        if(tr[x].vis[tx]==0) tr[x].vis[tx]=++num;
        x=tr[x].vis[tx];
    }
    tr[x].end=++tot;
}
int find(char a[])
{
    int len=strlen(a),x=0;
    for(int i=0;i<len;i++)
    {
        int tx=getnum(a[i]);
        x=tr[x].vis[tx];
    }
    return tr[x].end;
}
struct edge
{
    int u,v,w,c;
    int frm;
}e[20005];
int dis[205],head[205],vis[205];
int fr[205],flow;
void add(int u,int v,int w,int c)
{
    e[++cnt].u=head[u];e[cnt].v=v;
    e[cnt].w=w;e[cnt].c=c;
    e[cnt].frm=u;head[u]=cnt;
    
    e[++cnt].u=head[v];e[cnt].v=u;
    e[cnt].w=0;e[cnt].c=-c;
    e[cnt].frm=v;head[v]=cnt;
}
int spfa()
{
    for(int i=1;i<=t;i++) dis[i]=inf;
    memset(vis,0,sizeof(vis));
    queue<int>q;
    q.push(1);
    vis[1]=1;dis[1]=0;
    while(!q.empty())
    {
        int nxt=q.front();q.pop();
        for(int i=head[nxt];i!=-1;i=e[i].u)
        {
            int tmp=e[i].v;
            if(dis[tmp]>dis[nxt]+e[i].c&&e[i].w)
            {
                dis[tmp]=dis[nxt]+e[i].c;
                fr[tmp]=i;
                if(vis[tmp]==0)
                {
                    vis[tmp]=1;
                    q.push(tmp);
                }
            }
        }
        vis[nxt]=0;
    }
    if(dis[t]==inf) return 0;
    return 1;
}
int mcf()
{
    int re=0,x=inf;
    for(int i=fr[t];i;i=fr[e[i].frm]) x=min(e[i].w,x);
    for(int i=fr[t];i;i=fr[e[i].frm])
    {
        e[i].w-=x;
        e[i^1].w+=x;
        re+=x*e[i].c;
    }
    flow+=x;
    return re;
}
void dfs(int x)
{
    q[++sum]=x;
    for(int i=head[x];i!=-1;i=e[i].u)
    {
        if(e[i].w==0&&e[i].c<0)
        {
            dfs(e[i].v);
            e[i].w=1;
            return;
        }
    }
}
int main()
{
//    freopen("testdata.in","r",stdin);
    read(n);read(m);
    t=(n<<1);
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s[i]);
        build(s[i]);
        add(i,i+n,1,-1);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%s",st);
        l=find(st);
        scanf("%s",st);
        r=find(st);
        add(l+n,r,1,-1);
    }
    add(1,n+1,1,-1);add(n,n+n,1,-1);
    while(spfa()) ans+=mcf();
    if(flow==0)
    {
        printf("No Solution!");
        return 0;
    }
    else if(flow==1)
    {
        printf("2\n%s\n%s\n%s",s[1],s[n],s[1]);
        return 0;
    }
    printf("%d\n",(-ans)/2-1);
    dfs(1);
    for(int i=1;i<=sum;i++)
    {
        if(q[i]<=n) printf("%s\n",s[q[i]]);
    }
    sum=0;
    dfs(1);
    for(int i=sum-2;i;i--)
    {
        if(q[i]<=n) printf("%s\n",s[q[i]]);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/zeroform/p/8574246.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值