1. 程式人生 > >【ZROI 537】貪心題 題解

【ZROI 537】貪心題 題解

【ZROI 537】貪心題 題解

Solution

最大的一邊直接放到一起貪心即可

著重講小的一邊

已知對於二分圖匹配,其答案即為最大流

令時間集合為 \(T = {1,2,3,\dots,maxt}\)

對於每一門課程,按照如下方式建圖:

  • 每個任務為一個點,每個時間為一個點,每個任務向其對應的時間區間連邊,源點向每個任務連邊,邊權為 \(1\),每個時間向匯點連邊,邊權為 \(1\)

考慮第一門課程:

我們選擇一些時間節點分給它,設為 \(T_1\)

假設最大流中任務集合為 \(A\),對應的時間集合為 \(B\),並且 \(|A| =|B|=二分圖匹配數量\)

那麼根據最大流-最小割定理,相當於在圖中割去 \(|A|\) 條邊,滿足下述性質:

  • 沒有從源點到匯點的合法路徑

考慮第二門課程:

分給它的時間節點為 \(T_2=\complement _T ^{T_1}\)

這時候假設最大流中任務集合為 \(C\),對應的時間集合為 \(D\),並且 \(|C|=|D|=二分圖匹配數量\)

同樣擁有上述性質

下面建立新的一張圖:

將上述兩張圖並起來,把左邊的匯點和右邊的源點去掉,每個左邊的時間節點向右邊對應的時間節點連邊權為 \(1\) 的一條邊

下面證明,新圖中的每一個割和原來兩個圖中某兩個割的並等價。

  1. 將原圖中的兩個割放到現在的圖上,把左邊的圖的時間節點到匯點的割變成現在的時間節點到對應時間節點的割,右邊類似,由於 \(T_1 \bigcap T_2 = \varnothing\)
    ,所以不會有一條中間的邊被割兩次,那麼,對於中間的任意一條邊,要麼源點無法走到它,要麼它無法走到匯點(否則無論它分給哪個課程都不能形成割),所以現在仍舊是一個割
  2. 考慮現在的任意一個割,按照上面的方法把它對應到原來的兩張圖上,對於每一箇中間的邊,要麼源點不能走向它,要麼它不能走向匯點,如果源點不能到他就把它分給源點,否則把它分給匯點,這樣原來的兩張圖仍舊滿足割的性質
  3. 綜上,命題得證

所以新圖的最小割即為答案

Code

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
#define Debug(...) fprintf(stderr, __VA_ARGS__)

ll read(){
  ll x=0,f=1;char c=getchar();
  while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
  while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
  return x*f;
}

const int maxn = 44000;
int n, m;
pii p[maxn], tp[maxn];
struct lzt {
  int fi, se, ii;
  bool operator < (const lzt &b) const {
    if (fi != b.fi) return fi < b.fi;
    if (se != b.se) return se < b.se;
    return ii < b.ii;
  }
};
lzt a[maxn], b[maxn];
priority_queue<int, vector<int>, greater<int> > pq;

namespace task1 {
  const int mxst = 1100000;
  int f[22][mxst];
  void main() {
    int mx = (1 << n) - 1, mxt = 0;
    rep(i, 1, n) mxt = max(mxt, p[i].se);
    rep(i, 1, m) a[i].fi = p[i].fi, a[i].se = p[i].se, a[i].ii = i;
    rep(i, m + 1, n) b[i - m].fi = p[i].fi, b[i - m].se = p[i].se, b[i - m].ii = i;
    m = n - m; n = n - m;
    sort(a + 1, a + n + 1); sort(b + 1, b + m + 1);
    rep(i, 0, mxt) rep(j, 0, mx) f[i][j] = 1e9;
    f[0][0] = 0;
    rep(i, 0, mxt - 1) {
      rep(j, 0, mx) {
        if (f[i][j] == 1e9) continue;
        // 選擇第一門
        int ind = 0;
        rep(k, 1, n) {
          if (a[k].fi > i + 1) break;
          if (a[k].se < i + 1 || (j & (1 << (k - 1)))) continue;
          if (!ind || a[k].se < a[ind].se || (a[k].se == a[ind].se && a[k].ii < a[ind].ii)) ind = k;
        }
        int nwst = j, ad = 0;
        if (ind) nwst += (1 << (ind - 1)), ad = 1;
        f[i + 1][nwst] = min(f[i + 1][nwst], f[i][j] + ad);
        //選擇第二門
        int ind2 = 0;
        rep(k, 1, m) {
          if (b[k].fi > i + 1) break;
          if (b[k].se < i + 1 || (j & (1 << (n + k - 1)))) continue;
          if (!ind2 || b[k].se < b[ind2].se || (b[k].se == b[ind2].se && b[k].ii < b[ind2].ii)) ind2 = k;
        }
        int nwst2 = j; ad = 0;
        if (ind2) nwst2 += (1 << (n + ind2 - 1)), ad = 1;
        f[i + 1][nwst2] = min(f[i + 1][nwst2], f[i][j] + ad);
      }
    }
    int ans = 1e9;
    rep(i, 0, mx) ans = min(ans, f[mxt][i]);
    printf("%d\n", ans);
  }
}

namespace task2 {
  struct Dinic{
    struct Edge{
      int fr,to,cap,flow;
    };
    int n,m,s,t;
    vector<Edge> edges;
    vector<int> G[maxn];
    bool vis[maxn];
    int cur[maxn],d[maxn];
    
    void addedge(int u,int v,int cap){
      edges.pb((Edge){u,v,cap,0});
      edges.pb((Edge){v,u,0,0});
      m=edges.size();
      G[u].pb(m-2);G[v].pb(m-1);
    }
    
    bool BFS(){
      memset(vis,0,sizeof(vis));
      queue<int> q;
      q.push(s);
      vis[s]=1;
      d[s]=0;
      while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=0;i<G[u].size();i++){
          Edge &e=edges[G[u][i]];
          if(!vis[e.to] && e.cap>e.flow){
            vis[e.to]=1;
            d[e.to]=d[u]+1;
            q.push(e.to);
          }
        }
      }
      return vis[t];
    }
    
    int DFS(int x,int a){
      if(x==t || !a) return a;
      int f,flow=0;
      for(int &i=cur[x];i<G[x].size();i++){
        Edge &e=edges[G[x][i]];
        if(d[e.to]==d[x]+1 && (f=DFS(e.to,min(a,e.cap-e.flow)))>0){
          e.flow+=f;
          a-=f;flow+=f;
          edges[G[x][i]^1].flow-=f;
          if(!a) break;
        }
      }
      return flow;
    }
    
    int MaxFlow(int s,int t){
      this->s=s;this->t=t;
      int ret=0;
      while(BFS()){
        memset(cur,0,sizeof(cur));
        ret+=DFS(s,1e9);
      }
      return ret;
    }
  } gr;
  void main() {
    int mxt = 0;
    rep(i, 1, n) mxt = max(mxt, p[i].se);
    rep(i, 1, m) a[i].fi = p[i].fi, a[i].se = p[i].se, a[i].ii = i;
    rep(i, m + 1, n) b[i - m].fi = p[i].fi, b[i - m].se = p[i].se, b[i - m].ii = i;
    m = n - m; n = n - m;
    int s = n + m + mxt + mxt + 1, t = s + 1;
    sort(a + 1, a + n + 1); sort(b + 1, b + m + 1);
    rep(i, 1, n) gr.addedge(s, i, 1);
    rep(i, 1, m) gr.addedge(i + n, t, 1);
    rep(i, 1, mxt) gr.addedge(i + n + m, i + n + m + mxt, 1);
    rep(i, 1, mxt) {
      rep(j, 1, n) if (a[j].fi <= i && a[j].se >= i) gr.addedge(j, i + n + m, 1e9);
      rep(j, 1, m) if (b[j].fi <= i && b[j].se >= i) gr.addedge(i + n + m + mxt, j + n, 1e9);
    }
    printf("%d\n", gr.MaxFlow(s, t));
  }
}

void work(){
  n = read(), m = read();
  rep(i, 1, n) p[i].fi = read(), p[i].se = read();
  rep(i, 1, n) tp[i] = p[i];
  sort(tp + 1, tp + n + 1);
  int nw = 1, ans = 0;
  rep(i, 1, 400) {
    while (tp[nw].fi <= i && nw <= n) pq.push(tp[nw].se), nw++;
    while (!pq.empty() && pq.top() < i) pq.pop();
    if (!pq.empty()) ans++, pq.pop();
  }
  printf("%d\n", ans);
  task2::main();
}

int main(){
  #ifdef LZT
    freopen("in","r",stdin);
  #endif

  work();

  #ifdef LZT
    Debug("My Time: %.3lfms", (double)clock() / CLOCKS_PER_SEC);
  #endif
}