zoukankan      html  css  js  c++  java
  • Oooooooo AAAAE 【网络流最小点权覆盖】

    Description

     “Let the bass kick!O-oooooooooo AAAAE-A-A-I-A-U- JO-oooooooooooo AAE-O-A-A-U-U-A- E-eee-ee-eee AAAAE-A-E-I-E-A- JO-ooo-oo-oo-oo EEEEO-A-AAA-AAAA!”

    LiMn2O4沉迷音乐游戏,每天都在摸鱼搓音游,而且是手机电脑两开花……为了帮助LiMn2O4尽快清醒过来,LiMn2O4答应skyer_hxx,只要skyer_hxx能写一个自动脚本来玩这个游戏,打败他的最高纪录,那么就不再摸鱼了。

    这个游戏可以简化成:谱面由N个键和M条连线组成,每两个键之间有一个连线,玩家需要在键之间滑动,且连线的方向是固定的,玩家每次选择一个键,把所有从这个键出发的连线都消除掉,花费为ai,也可以将每个结束在这个点的连线消除,花费Bi。不用担心,LiMn2O4的手指足够多。最后让连线全部消失,得分就是总花费。花费越少,分数越高。

    skyer_hxx何许人也,怎么会怕这点小问题?但是他现在很忙,需要你的帮助。请你对于给出的谱面,求出最小的花费。

    Input

    第一行有两个正整数N,M,含义同题目描述

    接下来两行,第一行有N个正整数 ,代表从键i正向划到别的键需要的花费

    第二行有N个正整数 ,代表从其他的键划到第i个键需要的花费

    接下来M行,每一行都有两个正整数u,v,代表键u,v之间有一条u到v的连线。两个键之间可能有多个连线,同一个键之间也可能有连线。

    Output

    输出最小的花费,占一行。

    Sample Input

    3 2

    1 2 1

    2 1 2

    1 2

    1 3

    Sample Output

    2

    我的想法:

    1.首先想用贪心算法,存每条边然后比较out[a], in[b]。不过错的很离谱,答案要大了很多。原因是因为没看清楚题目。也就是上面我标红的地方。对于每个点只能计算一次最小的点权,用贪心的话点有重复也会重复计算进去。

    2.最小点权覆盖集:从x或者y集合中选取一些点,使这些点覆盖所有的边,并且选出来的点的权值尽可能小。最大点权独立集:在二分图中找到权值和最大的点集,使得它们之间两两没有边。

    3.借鉴了题解的思路。才知道是网络流最小点权覆盖问题设立源点s和t,s连边到点i,容量为i点的权值;点j连边到t,容量为j点权值;原二分图中的边容量为INF,求最大流即为最小点权覆盖。

    4.扩展:最大点权独立集可转化为最小点权覆盖问题,最大点权独立集 = 总权值 - 最小点权覆盖集

    题解思路:

    经典的网络流最小点权覆盖问题,首先拆点,分成左右两部分,源点向所有左节点连一条边,流量为删除出边的花费。所有右节点向汇点连一条边,流量为删除入边的费用。对于每条输入给定的边<u,v>,其在左边的点为u,v,右边的点为u’,v’,那么我们连一条<u,v’>的边,流量为∞(正无穷才不会对可行流上最小的容量产生限制)。跑一边最大流,得到的答案就是最小的费用。
    首先,如果没有花费的话,这道题就是简单的最小点覆盖集。加上了点权之后,我们就要将最小割的思想融合进去。要注意费用,一定要符合S->T的顺序

    代码:

      1 #include<stdio.h>
      2 #include<string.h>
      3 #include<queue>
      4 #include<algorithm>
      5 #define mem(a, b) memset(a, b, sizeof(a))
      6 const int inf = 0x3f3f3f3f;
      7 using namespace std;
      8 
      9 int n, m, cnt, head[250];
     10 int arr[110], brr[110];
     11 int dep[250];
     12 queue<int>Q;
     13 
     14 struct Edge
     15 {
     16     int to, next;
     17     int w; 
     18 }edge[20000];
     19 
     20 void add(int a, int b, int w)
     21 {
     22     edge[cnt].to = b;
     23     edge[cnt].w = w;
     24     edge[cnt].next = head[a];
     25     head[a] = cnt ++;
     26 }
     27 
     28 int bfs(int st, int ed) //dinic算法的优化之处,先进行分层 
     29 {
     30     if(st == ed)
     31         return 0;
     32     while(!Q.empty())
     33         Q.pop();
     34     mem(dep, -1);  //每次都初始化为未被分层 
     35     dep[st] = 1; //源点设置层次为1
     36     Q.push(st);
     37     while(!Q.empty())
     38     {
     39         int index = Q.front();
     40         Q.pop();
     41         for(int i = head[index]; i != -1; i = edge[i].next)
     42         {
     43             int to = edge[i].to;
     44             if(dep[to] == -1 && edge[i].w > 0)
     45             {
     46                 dep[to] = dep[index] + 1;
     47                 Q.push(to);
     48             }
     49         }
     50     }
     51     return dep[ed] != -1;//返回是否成功分层 
     52 }
     53 
     54 int dfs(int now, int ed, int cnt)
     55 {
     56     if(now == ed)
     57         return cnt;
     58     for(int i = head[now]; i != -1; i = edge[i].next)
     59     {
     60         int to = edge[i].to;
     61         if(dep[to] == dep[now] + 1 && edge[i].w > 0)
     62         {
     63             int flow = dfs(to, ed, min(edge[i].w, cnt));
     64             if(flow > 0)
     65             {
     66                 edge[i].w -= flow;
     67                 edge[i ^ 1].w += flow;
     68                 return flow;
     69             }
     70         }
     71     }
     72     return -1;
     73 }
     74 
     75 void dinic(int st, int ed)
     76 {
     77     long long ans = 0;
     78     while(bfs(st, ed))
     79     {
     80         while(1)
     81         {
     82             int inc = dfs(st, ed, inf);
     83             if(inc == -1)
     84                 break;
     85             ans += inc;
     86         }
     87     }
     88     printf("%lld
    ", ans);
     89 }
     90 
     91 int main()
     92 {
     93     mem(head, -1);
     94     scanf("%d%d", &n, &m);//n个点,m条边 
     95     for(int i = 1; i <= n; i ++) //起点向汇点 2 * n + 1建边
     96     {
     97         scanf("%d", &arr[i]);
     98         add(i + n, 2 * n + 1, arr[i]);
     99         add(2 * n + 1, i + n, 0);
    100     }
    101     for(int i = 1; i <= n; i ++)//源点 0 向终点建边 
    102     {
    103         scanf("%d", &brr[i]);
    104         add(0, i, brr[i]);
    105         add(i, 0, 0);
    106     }
    107     for(int i = 1; i <= m; i ++)//从源点方向向汇点方向建边
    108     {
    109         int a, b;
    110         scanf("%d%d", &a, &b);
    111         add(a, b + n, inf);
    112         add(b + n, a, 0);
    113     }
    114     //从源点向汇点跑一遍最大流
    115     dinic(0, 2 * n + 1); 
    116     return 0;
    117 }
    View Code
  • 相关阅读:
    js判断是否第一次访问跳转
    dt系统中tag如何使用like与%来进行模糊查询
    DT图库列表修改内容标题字数
    第二周冲刺第四天个人博客
    04《梦断代码》阅读笔记01
    第二周冲刺第三天个人博客
    03《构建之法》阅读笔记03
    第二周冲刺第二天个人博客
    02《构建之法》阅读笔记02
    第二周冲刺第一天个人博客
  • 原文地址:https://www.cnblogs.com/yuanweidao/p/10943133.html
Copyright © 2011-2022 走看看