• bzoj 1927 [Sdoi2010]星际竞速


    最小费用最大流。

    首先,由于赛车只能由每个星球飞往引力比它大的星球,所以双向边其实是单向边。由于所有连边都是从小到大连,所以图是一张DAG。

    大概就是“最小权路径覆盖”。

    将每个点拆点。

    源点向每个点的入点连一条容量为1费用为0的边。

    源点向每个点的出点连一条容量为1费用为瞬移到该点所需时间的边。

    每个点的出点向汇点连一条容量为1费用为0的边。

    对于每条边(i,j),从i点入点向j点出点连一条容量为1费用为航路所需时间的边。

    构造十分精妙。

    考虑由于求最大流,所以所有点的出点向汇点的连边都会流满。每个点的出点的到达方式即为此点的到达方式:

      1.由源点流来:指在某一时刻瞬移到该星球。

      2.由入点流来:指由其他星球沿航路到该星球。

    第二种方式正确性?流到该点出点的某入点对应的星球,在之前的某一时刻一定由某种合法方式达到过,追溯到头一定是某个瞬移到的点(因为图中没有环),“追溯”的过程就是这一条路径。注意为什么源点会向所有入点连费用为0的边,这条边的容量其实对应着这个点的出点到达后而产生的贡献。每个点的入点只连向它能到的出点,又由于图为DAG,所以一定存在必须瞬移(走源点到出点的边)的点,正确性也就显然了。换句话说,每个从源点直接流向出点的点是一条路径的起始点,它的入点因为出点的到达而有意义,之后就是整条路径的递推。

     1 #include<cstring>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<iostream>
     5 #include<queue>
     6 using namespace std;
     7 const int dian=1605;
     8 const int bian=40005;
     9 const int INF=0x3f3f3f3f;
    10 int h[dian],ver[bian],val[bian],cos[bian],nxt[bian],minn[dian],with[dian];
    11 int d[dian],v[dian];
    12 int n,m,aa,bb,cc,tot;
    13 int S,T;
    14 void add(int a,int b,int c,int d){
    15     tot++;ver[tot]=b;val[tot]=c;cos[tot]=d;nxt[tot]=h[a];h[a]=tot;
    16     tot++;ver[tot]=a;val[tot]=0;cos[tot]=-d;nxt[tot]=h[b];h[b]=tot;
    17 }
    18 bool tell(){
    19     memset(v,0,sizeof(v));
    20     memset(d,0x3f,sizeof(d));
    21     memset(with,0,sizeof(with));
    22     memset(minn,0x3f,sizeof(minn));
    23     queue<int>q;
    24     q.push(S);
    25     v[S]=1;
    26     d[S]=0;
    27     while(!q.empty()){
    28         int x=q.front();
    29         q.pop();
    30         v[x]=0;
    31         for(int i=h[x];i;i=nxt[i]){
    32             int y=ver[i];
    33             if(d[y]>d[x]+cos[i]&&val[i]){
    34                 d[y]=d[x]+cos[i];
    35                 minn[y]=min(minn[x],val[i]);
    36                 with[y]=i;
    37                 if(!v[y]){
    38                     v[y]=1;
    39                     q.push(y);
    40                 }
    41             }
    42         }
    43     }
    44     if(d[T]==0x3f3f3f3f)
    45         return 0;
    46     return 1;
    47 }
    48 int zeng(){
    49     for(int i=T;i!=S;i=ver[with[i]^1]){
    50         val[with[i]]-=minn[T];
    51         val[with[i]^1]+=minn[T];
    52     }
    53     return d[T]*minn[T];
    54 }
    55 int dinic_cost(){
    56     int r=0;
    57     while(tell())
    58         r+=zeng();
    59     return r;
    60 }
    61 int main(){
    62     memset(h,0,sizeof(h));
    63     memset(nxt,0,sizeof(nxt));
    64     tot=1;
    65     scanf("%d%d",&n,&m);
    66     S=2*n+1,T=2*n+2;
    67     for(int i=1;i<=n;i++){
    68         scanf("%d",&aa);
    69         add(S,i+n,1,aa);
    70     }
    71     for(int i=1;i<=m;i++){
    72         scanf("%d%d%d",&aa,&bb,&cc);
    73         if(aa>bb)
    74             aa^=bb^=aa^=bb;
    75         add(aa,bb+n,1,cc);
    76     }
    77     for(int i=1;i<=n;i++){
    78         add(S,i,1,0);
    79         add(i+n,T,1,0);
    80     }
    81     printf("%d",dinic_cost());
    82     return 0;
    83 }
  • 相关阅读:
    Pascal 语言中的关键字及保留字
    单元文件结构
    在 case 语句中使用字符串-转
    程序流程的辅助控制-转
    XE版本 InputQuery 可以同时填多个输入值
    转:Delphi 6 实用函数
    转:Delphi 函数大全
    d 属性: 赋予字段执行动作的能力
    Json格式示意图
    转:虚拟方法跳过父类继承调用祖父类的代码 --值得试一试
  • 原文地址:https://www.cnblogs.com/dugudashen/p/6224772.html
Copyright © 2020-2023  润新知