【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 igcap 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
", 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
", 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
", 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
}