zoukankan      html  css  js  c++  java
  • [hdu6978]New Equipments II

    显然可以费用流来做,具体建图如下——

    点集:源点,汇点,左边$n$​个工人,右边$n$​​​个设备

    边集:源点向第$i$​个工人连$(1,a_{i})$​的边,第$i$​个设备向汇点连$(1,b_{i})$​​​的边,工人向可用的设备连$(1,0)$​的边

    跑最小费用最大流,流量为$i$时的费用即为$i$时的答案

    但注意到要跑$n$次spfa,每一次的最坏复杂度为$o(n^{3})$,显然无法通过

    实际上,spfa即找到$x$和$y$,使得第$x$个工人和第$y$个设备都未被使用过,且第$x$个工人能流到第$y$个设备,在此基础上最大化$a_{x}+b_{y}$​,那么不妨手动来实现此功能

    考虑按照$a_{i}$​​从大到小枚举左边未被使用过的的工人,并递归其能流到的点,求出其中设备$b_{i}$​的最大值

    注意到当一个点已经被访问过,显然再访问一定不优于之前,单次复杂度即降为$o(n^{2})$​

    每一个点只会被访问一次,因此复杂度瓶颈在于遍历边集,使用bfs来代替dfs,并对点分类讨论:

    1.对于工人,其几乎到所有设备都有边,缺的仅有$o(m)$条限制和$o(n)$条之前流过的边,因此只需要遍历所有当前未被访问的点,并搜索其中可以访问的点

    注意到一个未访问的点被遍历,要么被访问,要么属于$o(n+m)$​​条边之一,因此总复杂度即$o(m)$​​​

    2.对于设备,其出边只有$o(n)$条之前流过的边的反向边,暴力遍历边集即可,总复杂度即$o(n)$

    由此,单次复杂度降为$o(m)$​​,总复杂度即$o(nm)$​​,可以通过

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define N 4005
      4 #define ll long long
      5 queue<int>q;
      6 vector<int>v[N];
      7 int t,n,m,x,y,a[N],b[N],id[N],visa[N],visb[N],vis[N],nex[N],pre[N<<1],mx[N],e[N][N];
      8 ll ans;
      9 bool cmp(int x,int y){
     10     return a[x]>a[y];
     11 }
     12 int bfs(int k){
     13     int s=0;
     14     q.push(k);
     15     vis[k]=1;
     16     while (!q.empty()){
     17         int k=q.front();
     18         q.pop();
     19         if (k<=n){
     20             for(int i=nex[0],j=0;i<=n;j=i,i=nex[i])
     21                 if (e[k][i]){
     22                     pre[i+n]=k;
     23                     q.push(i+n);
     24                     nex[j]=nex[i],i=j;
     25                 }
     26         }
     27         else{
     28             k-=n;
     29             if ((!visb[k])&&(b[s]<b[k]))s=k;
     30             for(int i=0;i<v[k].size();i++)
     31                 if (!vis[v[k][i]]){
     32                     pre[v[k][i]]=k+n;
     33                     q.push(v[k][i]);
     34                     vis[v[k][i]]=1;
     35                 }
     36         }
     37     }
     38     return s;
     39 }
     40 int calc(){
     41     int ans=0;
     42     for(int i=1;i<=n;i++)vis[i]=0;
     43     for(int i=0;i<=n;i++)nex[i]=i+1;
     44     for(int i=1;i<=(n<<1);i++)pre[i]=0;
     45     for(int i=1;i<=n;i++){
     46         mx[i]=0;
     47         if ((!visa[id[i]])&&(!vis[id[i]])){
     48             mx[i]=bfs(id[i]);
     49             if (mx[i])ans=max(ans,a[id[i]]+b[mx[i]]);
     50         }
     51     }
     52     if (!ans)return 0;
     53     for(int i=1;i<=n;i++)
     54         if ((mx[i])&&(a[id[i]]+b[mx[i]]==ans)){
     55             visa[id[i]]=1,visb[mx[i]]=1;
     56             for(int lst=mx[i]+n,j=pre[lst];j;lst=j,j=pre[j]){
     57                 if (j<=n){
     58                     e[j][lst-n]=0;
     59                     v[lst-n].push_back(j);
     60                 }
     61                 else{
     62                     e[lst][j-n]=1;
     63                     for(int k=0;k<v[j-n].size();k++)
     64                         if (v[j-n][k]==lst){
     65                             v[j-n].erase(v[j-n].begin()+k);
     66                             break;
     67                         }
     68                 }
     69             }
     70             break;
     71         }
     72     return ans;
     73 }
     74 int main(){
     75     scanf("%d",&t);
     76     while (t--){
     77         scanf("%d%d",&n,&m);
     78         ans=0;
     79         for(int i=1;i<=n;i++){
     80             visa[i]=visb[i]=0; 
     81             v[i].clear();
     82         }
     83         for(int i=1;i<=n;i++)
     84             for(int j=1;j<=n;j++)e[i][j]=1;
     85         for(int i=1;i<=n;i++){
     86             scanf("%d",&a[i]);
     87             id[i]=i;
     88         }
     89         sort(id+1,id+n+1,cmp);
     90         for(int i=1;i<=n;i++)scanf("%d",&b[i]);
     91         for(int i=1;i<=m;i++){
     92             scanf("%d%d",&x,&y);
     93             e[x][y]=0;
     94         }
     95         for(int i=1;i<=n;i++){
     96             int s=calc();
     97             if (!s){
     98                 for(;i<=n;i++)printf("-1
    ");
     99                 break;
    100             }
    101             ans+=s;
    102             printf("%lld
    ",ans);
    103         }
    104     }
    105     return 0;
    106 }
    View Code
  • 相关阅读:
    Linux(实操篇)--- 实用指令-运行级别和找回root密码
    Python使用文件操作实现一个XX信息管理系统的示例
    python解释器安装教程的方法步骤
    python如何使用代码运行助手
    python 识别登录验证码图片功能的实现代码(完整代码)
    python线性插值解析
    python协程 详解
    maxcompute mapjoin
    spark split节点笔记
    安装和配置hadoop
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/15087918.html
Copyright © 2011-2022 走看看