【ZROI 537】贪心题 题解

【ZROI 537】贪心题 题解

Link

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
}

原文地址:https://www.cnblogs.com/wawawa8/p/10206686.html

时间: 2024-10-09 12:34:30

【ZROI 537】贪心题 题解的相关文章

一些贪心题的题解

众所周知,贪心是一个比较恶心的算法. 区别于常规的算法或数据结构,贪心题一般不会让人看到就想到思路,而是需要我们在一些猜测或是感性分析下,找到一种局部最优的方案,并且可以通过局部最优解推出全局最优解. T1:防晒 有C头奶牛进行日光浴,第i头奶牛需要minSPF[i]到maxSPF[i]单位强度之间的阳光. 每头奶牛在日光浴前必须涂防晒霜,防晒霜有L种,涂上第i种之后,身体接收到的阳光强度就会稳定为SPF[i],第i种防晒霜有cover[i]瓶. 求最多可以满足多少头奶牛进行日光浴. 输入格式

HDU 1661 Assigments 贪心法题解

Problem Description In a factory, there are N workers to finish two types of tasks (A and B). Each type has N tasks. Each task of type A needs xi time to finish, and each task of type B needs yj time to finish, now, you, as the boss of the factory, n

CSDN高校俱乐部第二届战神杯第二题题解

两个人玩一个数字游戏,给定两个正整数A,B,两个人轮流从一个数中减去另外一个数的正数倍,要保证结果非负, 首先得到0的人获胜.例如:30 8经过一步操作可以变为22 8 或者14 8 或者 6 8.两个人都足够聪明. 输入格式: 多组数据,每组数据占一行是两个空格分隔的正整数(在32位整书范围内) 输出格式: 每组数据一行,输出是第一个人赢,还是第二个人赢. 输入: 3 5 4 5 输出: 1 2 思路:如果你知道连分数,这题就是水题一道了.(记得某神说,菜鸟做完一题总是说这题很简单!没错,我就

CF#248A、B题题解

第一次不用翻译来打CF,中国人的英文有共鸣啊^_^ 不过没做出C题,很可惜啊.D题看了不敢碰,我对mod10^9+7的题目有阴影... A题,题意是一堆苹果,能不能分成两堆一样重的,我一开始没读清楚题目,拍了个背包(以总和一般作背包容量是否能刚好装满),后来看真一点,才发现只有两种重量的,一种是100,一种是200.之后我就YY了,是不是sum%200==0就输出YES,否则就是NO..我还无耻地交了,获得了一个WA,马上想明白了,200,200,200明显即不对啦.之后继续YY-思路就是计算2

杭电 2187 (贪心题)悼念512汶川大地震遇难同胞——老人是真饿了

http://acm.hdu.edu.cn/showproblem.php?pid=2187 悼念512汶川大地震遇难同胞--老人是真饿了 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 7286    Accepted Submission(s): 3043 Problem Description 时间:2008年5月16日(震后第4

【题解整理】西安邀请赛部分题题解

很抱歉,学校的OJ并不支持外网,而且还没有加上题目... A 字符串基本处理 http://blog.csdn.net/polossk/article/details/27113175 B 状态压缩加剪枝,但是我们没敢做 C 单源点最短路 http://blog.csdn.net/polossk/article/details/27113385 D 字符串生成器 深搜 http://blog.csdn.net/polossk/article/details/27120395 J 状态压缩DP h

POJ 2608 Soundex 基础题题解

基本的编程能力考查. 注意: 1 下标处理 2 审查题意,并严格根据题意去重. 3 如何把代码写清晰精简. #include <stdio.h> #include <string.h> const short MAX_LETTER = 21; const short ALP_LEN = 26; short Letter[ALP_LEN] = {-1, 1, 2, 3, -1, 1, 2, -1, -1, 2, 2, 4, 5, 5, -1, 1, 2, 6, 2, 3, -1, 1

10.9 guz模拟题题解

感谢@guz 顾z的题题解 考试共三道题,其中 第一题help共10个测试点,时间限制为 1000ms,空间限制为 256MB. 第二题escape共20个测试点,时间限制为1000ms2000ms,空间限制1MB16MB. 第三题cure共20个测试点,时间限制为 1000ms,空间限制为 256MB. PS:题目背景与题目本身无关,可跳过 help 题目背景 在梦中,王 小 呆 到了一个神秘的地方.这里是昆虫王国!国王见王 小 呆 好生俊俏,便给了他一杯昆虫国的老白干.王 小 呆一口下肚,突

2018-2019赛季多校联合新生训练赛第六场(2018/12/15)补题题解

A 价钱统计(基础编程能力) 这个考点还是比较个性的,怎么四舍五入 解法 常规的讲如果四舍五入整数位的话,那么只需要在后面加个0.5然后强制转换一下就可以了 这个却要我们保留一位小数的四舍五入,那该怎么做呢 实际上我们只需要把这个数乘以10然后加0.5,强制转换后再除以10就可以了 代码 #include <bits/stdc++.h> using namespace std; double trans(double a) { a*=10; a+=0.5; a=int(a); a/=10; r