【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\) 的一条边
下面证明,新图中的每一个割和原来两个图中某两个割的并等价。
- 将原图中的两个割放到现在的图上,把左边的图的时间节点到汇点的割变成现在的时间节点到对应时间节点的割,右边类似,由于 \(T_1 \bigcap T_2 = \varnothing\),所以不会有一条中间的边被割两次,那么,对于中间的任意一条边,要么源点无法走到它,要么它无法走到汇点(否则无论它分给哪个课程都不能形成割),所以现在仍旧是一个割
- 考虑现在的任意一个割,按照上面的方法把它对应到原来的两张图上,对于每一个中间的边,要么源点不能走向它,要么它不能走向汇点,如果源点不能到他就把它分给源点,否则把它分给汇点,这样原来的两张图仍旧满足割的性质
- 综上,命题得证
所以新图的最小割即为答案
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
}
【ZROI 537】贪心题 题解的更多相关文章
- ACM ICPC 2018 青岛赛区 部分金牌题题解(K,L,I,G)
目录: K Airdrop I Soldier Game L Sub-cycle Graph G Repair the Artwork ———————————————————— ps:楼主脑残有点严 ...
- Codeforces Round #609 (Div. 2)前五题题解
Codeforces Round #609 (Div. 2)前五题题解 补题补题…… C题写挂了好几个次,最后一题看了好久题解才懂……我太迟钝了…… 然后因为longlong调了半个小时…… A.Eq ...
- NOIP2018初赛普及组原题&题解
NOIP2018初赛普及组原题&题解 目录 NOIP2018初赛普及组原题&题解 原题&答案 题解 单项选择题 第$1$题 第$2$题 第$3$题 第$4$题 第$5$题 第$ ...
- 10.9 guz模拟题题解
感谢@guz 顾z的题题解 考试共三道题,其中 第一题help共10个测试点,时间限制为 1000ms,空间限制为 256MB. 第二题escape共20个测试点,时间限制为1000ms2000ms, ...
- Codeforces Round #599 (Div. 2)的简单题题解
难题不会啊…… 我感觉写这个的原因就是因为……无聊要给大家翻译题面 A. Maximum Square 简单题意: 有$n$条长为$a_i$,宽为1的木板,现在你可以随便抽几个拼在一起,然后你要从这一 ...
- Codeforces Round #612 (Div. 2) 前四题题解
这场比赛的出题人挺有意思,全部magic成了青色. 还有题目中的图片特别有趣. 晚上没打,开virtual contest打的,就会前三道,我太菜了. 最后看着题解补了第四道. 比赛传送门 A. An ...
- Hello2020(前四题题解)
Hello,2020!新的一年从快乐的掉分开始…… 我在m3.codeforces.com这个镜像网站中一开始还打不开D题,我…… 还有话说今天这场为什么那么多二分. 比赛传送门:https://co ...
- Codeforces Round #524 (Div. 2)(前三题题解)
这场比赛手速场+数学场,像我这样读题都读不大懂的蒟蒻表示呵呵呵. 第四题搞了半天,大概想出来了,但来不及(中途家里网炸了)查错,于是我交了两次丢了100分.幸亏这次没有掉rating. 比赛传送门:h ...
- Educational Codeforces Round 53 (Rated for Div. 2) (前五题题解)
这场比赛没有打,后来补了一下,第五题数位dp好不容易才搞出来(我太菜啊). 比赛传送门:http://codeforces.com/contest/1073 A. Diverse Substring ...
随机推荐
- IE不支持HTML5表单属性placeholder的解决办法
1. [代码][JavaScript]代码 (function ($) { $.fn.placeholder = function (options) { var defaults ...
- navicat for mysql 安装
直接上正题,用于记录 1.下载linux版本的navicat:http://www.navicat.com/download/navicat-for-mysql 2.解压 tar -vzxf navi ...
- ntp服务器同步时间详细配置
部署NTP服务器进行时间同步 NTP服务端:linl_S IP:10.0.0.15 NTP客户端:lin_C IP:10.0.0.16 NTP服务概述 1.原理 NTP(Network ...
- ls命令还能这么玩
排序文件大小: 我们希望以文件大小排序,我们可以使用-S 参数来这么做 如果希望文件大小从小到大排序: 如果只希望列出目录条目: 增加 /(斜线) 标记目录:要这么做,使用-p选项: 通过修改时间列出 ...
- MySQL计算指标连续两月金额相比_20160929
在正常的业务逻辑中,对客户的分析是必须的也是最经常用到的,根据时间维度计算计算指标连续两月环比情况也是一道必须面对的题目. 国庆放假 先写代码 后面再拆分解释 SELECT a.*,b.年月 AS 上 ...
- 【C++基础】浅拷贝和深拷贝
简单理解: 对于一块内存,浅拷贝只是增加了一个指针,这样两个变量都指向这块内存,二深拷贝则是先开辟一块同等大小的新内存区,将待拷贝内存的内容复制过来,再赋予一个指向新内存的指针.区别在于:浅拷贝会造成 ...
- MongoDB中的一些坑(最好不要用)
MongoDB 是目前炙手可热的 NoSQL 文档型数据库,它提供的一些特性很棒:如自动 failover 机制,自动 sharding,无模式 schemaless,大部分情况下性能也很棒.但是薄荷 ...
- 简单两步快速实现shiro的配置和使用,包含登录验证、角色验证、权限验证以及shiro登录注销流程(基于spring的方式,使用maven构建)
前言: shiro因为其简单.可靠.实现方便而成为现在最常用的安全框架,那么这篇文章除了会用简洁明了的方式讲一下基于spring的shiro详细配置和登录注销功能使用之外,也会根据惯例在文章最后总结一 ...
- Day07:常用模块,面向对象编程(对象&类)及内置函数
今日内容:1.常用模块2.面向对象编程(*****) 介绍面向对象编程 类 对象3.内置函数------------------------------1.面向过程编程 核心“ ...
- 【转】Pro Android学习笔记(三):了解Android资源(上)
在Android开发中,资源包括文件或者值,它们和执行应用捆绑,无需在源代码中写死,因此我们可以改变或替换他们,而无需对应用重新编译. 了解资源构成 参考阅读Android学习笔记(三八):资源res ...