• [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
    ",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
    %s
    %s
    %s",s[1],s[n],s[1]);
            return 0;
        }
        printf("%d
    ",(-ans)/2-1);
        dfs(1);
        for(int i=1;i<=sum;i++)
        {
            if(q[i]<=n) printf("%s
    ",s[q[i]]);
        }
        sum=0;
        dfs(1);
        for(int i=sum-2;i;i--)
        {
            if(q[i]<=n) printf("%s
    ",s[q[i]]);
        }
        return 0;
    }
  • 相关阅读:
    客商支付明细SQL_billdate
    两张表判断赋值,都是NULL惹的祸…
    DataGridView使用初步
    在SQL Server 2005中启用“SQL Server”身份验证
    .Net学习笔记——细节问题
    C#调用带返回值的存储过程
    利用ASP.NET一般处理程序动态生成Web图像
    Windows Forms数据绑定技术
    C#中产生SQL语句的几种方式
    风讯dotNETCMS源码分析—数据存取篇
  • 原文地址:https://www.cnblogs.com/zeroform/p/8574246.html
Copyright © 2020-2023  润新知