• HDU5772 String problem 最大权闭合图+巧妙建图


    题意:自己看吧(不是很好说)

    分析:

    网络流:最大权闭合子图。

    思路如下:

    首先将点分为3类

    第一类:Pij 表示第i个点和第j个点组合的点,那么Pij的权值等于w[i][j]+w[j][i](表示得分)

    第二类:原串中的n个点每个点拆出一个点,第i个点权值为 –a[s[i]] (表示要花费)

    第三类:对于10种字符拆出10个点,每个点的权值为  -(b[x]-a[x])

    那么我们可以得到一个关系图 ,对于第一类中的点Pij,如果想要选择Pij,你就必须要选中第二类中的点i和j,对于第二类中的点如果你想选中第i个点,其对应的字符s[i],那么就必须选中第三类中s[i] 对应的点,因为每个种类的点第一次选中时花费是b[s[i]],而第二类中花费都是a[s[i]],一定要补上b[s[i]]-a[s[i]],而且只需要补上一次。

    得到上面的关系图后然后就是普通的最大权闭合子图问题,直接求解即可。

    注:吐槽,我想说真的是神思路,加网络流大法好,我只想说没有刷过网络流24题的人伤不起啊

         首先想会做这个题,必须会最大权闭合子图的概念,这个是网络流24题之一(多刷题就是好),但是就算是会了这个建图,也不一定会做这个题

        虽然说这个题看数据范围什么的,限制条件这么多的,差不多能想到是网络流,但是伤在不会建图啊

        这个题很巧妙的点,就是一个子序列的贡献是每两个不同点组成的,这是独立的,只和这两个元素有关,和序列的其它元素无关,这样每两个元素就可以虚拟一个点了

        然后按照题解建成有向图,对于u-->v,选u必须选v 对于这种图的求最大方案,显然就是求最大权闭合图

        如何建图,请见http://www.cnblogs.com/kane0526/archive/2013/04/05/3001557.html(可以顺便把这个题做一下,是个裸题)

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <vector>
    using namespace std;
    typedef  long long LL;
    const int INF = 0x3f3f3f3f;
    const int maxn=4e4+5;
    struct Edge
    {
        int from,to,cap,flow;
        Edge(int u,int v,int c,int d):from(u),to(v),cap(c),flow(d) {}
    };
    struct dinic
    {
        int s,t;
        vector<Edge>edges;
        vector<int>G[maxn];
        int d[maxn];
        int cur[maxn];
        bool vis[maxn];
        void init(){
          for(int i=0;i<maxn;++i)G[i].clear();
          edges.clear();
        }
        bool bfs()
        {
            memset(vis,0,sizeof(vis));
            queue<int>q;
            q.push(s);
            d[s]=0;
            vis[s]=1;
            while(!q.empty())
            {
                int x=q.front();
                q.pop();
                for(int i=0; i<G[x].size(); i++)
                {
                    Edge &e= edges[G[x][i]];
                    if(!vis[e.to]&&e.cap>e.flow)
                    {
                        vis[e.to]=1;
                        d[e.to]=d[x]+1;
                        q.push(e.to);
                    }
                }
            }
            return vis[t];
        }
        int dfs(int x,int a)
        {
            if(x==t||a==0)return a;
            int flow=0,f;
            for(int &i=cur[x]; i<G[x].size(); i++)
            {
                Edge &e=edges[G[x][i]];
                if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow))))
                {
                    e.flow+=f;
                    edges[G[x][i]^1].flow-=f;
                    flow+=f;
                    a-=f;
                    if(a==0)break;
                }
            }
            return flow;
        }
        LL maxflow(int s,int t)
        {
            this->s=s;
            this->t=t;
            LL flow=0;
            while(bfs())
            {
                memset(cur,0,sizeof(cur));
                flow+=dfs(s,INF);
            }
            return flow;
        }
        void addedge(int u,int v,int c)
        {
            Edge x(u,v,c,0),y(v,u,0,0);
            edges.push_back(x);
            edges.push_back(y);
            int l=edges.size();
            G[u].push_back(l-2);
            G[v].push_back(l-1);
        }
    }solve;
    int a[11],b[11],T,n,w[102][102],kase;
    char s[105];
    int main(){
      scanf("%d",&T);
      while(T--){
         solve.init();
         scanf("%d%s",&n,s+1);
         for(int i=1;i<=10;++i)
            scanf("%d%d",&a[i],&b[i]);
         for(int i=1;i<=n;++i)
          for(int j=1;j<=n;++j)
            scanf("%d",&w[i][j]);
         int cnt=0,p=n*(n-1)/2;
         LL sum=0;
         for(int i=1;i<n;++i){
           for(int j=i+1;j<=n;++j){
             ++cnt;
             if(w[i][j]+w[j][i]>0){
              solve.addedge(0,cnt,w[i][j]+w[j][i]);
              sum+=w[i][j]+w[j][i];
             }
             solve.addedge(cnt,p+i,INF);
             solve.addedge(cnt,p+j,INF);
           }
         }
         int t=cnt+n+11;
         for(int i=1;i<=n;++i){
          if(a[s[i]-'0'+1]>0)
           solve.addedge(cnt+i,t,a[s[i]-'0'+1]);
           solve.addedge(cnt+i,cnt+n+s[i]-'0'+1,INF); 
         }
         for(int i=cnt+n+1;i<=cnt+n+10;++i)
            if(b[i-cnt-n]-a[i-cnt-n]>0)
          solve.addedge(i,t,b[i-cnt-n]-a[i-cnt-n]);
         printf("Case #%d: %I64d
    ",++kase,sum-solve.maxflow(0,t));
      }
      return 0;
    }
    View Code
  • 相关阅读:
    树链剖分求LCA
    洛谷P1019 单词接龙
    洛谷P1441 砝码称重
    洛谷P2347 砝码称重
    洛谷P1164 小A点菜
    洛谷P2202 [USACO13JAN]方块重叠Square Overlap
    黑客与画家 第四章
    黑客与画家 第十二章
    记录最近一段的体会
    11.5最小生成树(Minimum Spanning Trees)
  • 原文地址:https://www.cnblogs.com/shuguangzw/p/5719703.html
Copyright © 2020-2023  润新知