51nod 算法马拉松4 D装盒子(网络流 / 二分图最优匹配)

装盒子

基准时间限制:1 秒 空间限制:131072 KB 分值: 160

有n个长方形盒子,第i个长度为Li,宽度为Wi,我们需要把他们套放。注意一个盒子只可以套入长和宽分别不小于它的盒子,并且一个盒子里最多只能直接装入另外一个盒子 (但是可以不断嵌套),例如1 * 1 可以套入2 * 1,而2 * 1再套入2 * 2。套入之后盒子占地面积是最外面盒子的占地面积。给定N个盒子大小,求最终最小的总占地面积。

Input

第一行一个数N表示盒子的个数。
接下来N行,每行两个正整数,表示每个盒子的长度和宽度。
所有整数都是正的(N,以及盒子的长宽),且不超过200。

Output

一行一个整数表示最终最小的占地面积。

Input示例

3
1 1
1 2
2 1

Output示例

4

想了很久,发现具有二分图的性质:每个点只能被包含在另外一个点当中。那么拆点,然后A可以被包含在B中就由左侧的A向右侧的B建一条边,这时构成一个二分图.由于题目要求是的占地面积最小,那么考虑最大匹配时的连边情况,若最大匹配有A->B的这一条边存在,也就说明A被放在了B中,那么占地面积是不要包括A的面积的,所以考虑连边时,将A->B的边权值设为A的面积,那么最后答案就是所有点面积的总和 减去 最优匹配的权值和。 这里n<=200,所以考虑要使用N^3的最优匹配.

同样可以用二分图的题一定可以用网络流,然后网络流跑了一发(最小费用最大流)。最后二分图最优匹配32ms,而网络流64ms
  1 #pragma comment(linker, "/STACK:1677721600")
  2 #include <map>
  3 #include <set>
  4 #include <stack>
  5 #include <queue>
  6 #include <cmath>
  7 #include <ctime>
  8 #include <bitset>
  9 #include <vector>
 10 #include <cstdio>
 11 #include <cctype>
 12 #include <cstring>
 13 #include <cstdlib>
 14 #include <iostream>
 15 #include <algorithm>
 16 using namespace std;
 17 #define INF 0x3f3f3f3f
 18 #define inf (-((LL)1<<40))
 19 #define lson k<<1, L, (L + R)>>1
 20 #define rson k<<1|1,  ((L + R)>>1) + 1, R
 21 #define mem0(a) memset(a,0,sizeof(a))
 22 #define mem1(a) memset(a,-1,sizeof(a))
 23 #define mem(a, b) memset(a, b, sizeof(a))
 24 #define FIN freopen("in.txt", "r", stdin)
 25 #define FOUT freopen("out.txt", "w", stdout)
 26 #define rep(i, a, b) for(int i = a; i <= b; i ++)
 27 #define dec(i, a, b) for(int i = a; i >= b; i --)
 28
 29 //typedef __int64 LL;
 30 typedef long long LL;
 31 typedef pair<int, int> Pair;
 32 const int MAXN = 200 + 10;
 33 const int MAXM = 110000;
 34 const double eps = 1e-12;
 35 LL MOD = 1000000007;
 36
 37 int n;
 38
 39 struct KM {
 40     const static int maxn = 1e3 + 7;
 41     int A[maxn], B[maxn];
 42     int visA[maxn], visB[maxn];
 43     int match[maxn], slack[maxn], Map[maxn][maxn];
 44     int M, H;
 45
 46     void add(int u, int v, int w) {
 47         Map[u][v] = w;
 48     }
 49     bool find_path ( int i ) {
 50         visA[i] = true;
 51         for ( int j = 0; j < H; j++ ) {
 52             if ( !visB[j] && A[i] + B[j] == Map[i][j] ) {
 53                 visB[j] = true;
 54                 if (match[j] == -1 || find_path(match[j])) {
 55                     match[j] = i;
 56                     return true;
 57                 }
 58             } else if ( A[i] + B[j] > Map[i][j] ) //j属于B,且不在交错路径中
 59                 slack[j] = min(slack[j], A[i] + B[j] - Map[i][j]);
 60         }
 61         return false;
 62     }
 63
 64     int solve (int M, int H) {
 65         this->M = M; this->H = H;
 66         int i, j, d;
 67         memset(A, 0, sizeof(A));
 68         memset(B, 0, sizeof(B));
 69         memset(match, -1, sizeof(match));
 70         for ( i = 0; i < M; i++ )
 71             for ( j = 0; j < H; j++ )
 72                 A[i] = max (Map[i][j], A[i]);
 73         for ( i = 0; i < M; i++ ) {
 74             for ( j = 0; j < H; j++ )
 75                 slack[j] = INF;
 76             while ( 1 ) {
 77                 memset(visA, 0, sizeof(visA));
 78                 memset(visB, 0, sizeof(visB));
 79                 if ( find_path ( i ) ) break; //从i点出发找到交错路径则跳出循环
 80                 for ( d = INF, j = 0; j < H; j++ ) //取最小的slack[j]
 81                     if (!visB[j] && d > slack[j]) d = slack[j];
 82                 for ( j = 0; j < M; j++ ) //集合A中位于交错路径上的-d
 83                     if ( visA[j] ) A[j] -= d;
 84                 for ( j = 0; j < H; j++ ) //集合B中位于交错路径上的+d
 85                     if ( visB[j] ) B[j] += d;
 86                     else slack[j] -= d; //注意修改不在交错路径上的slack[j]
 87             }
 88         }
 89         int res = 0;
 90         for ( j = 0; j < H; j++ )
 91             res += Map[match[j]][j];
 92         return res;
 93     }
 94 }km;//点从0开始编号
 95
 96 struct Node {
 97     int w, h;
 98     bool operator < (const Node &A) const {
 99         if(w != A.w) return w < A.w;
100         return h < A.h;
101     }
102     bool operator == (const Node &A) const {
103         return w == A.w && h == A.h;
104     }
105 }r[MAXN];
106
107 void handle() {
108     sort(r + 1, r + n + 1);
109     int cnt = 1;
110     rep (i, 2, n) if(!(r[i] == r[i - 1])) {
111         r[++cnt] = r[i];
112     }
113     n = cnt;
114 }
115
116 int solve() {
117     int ans = 0;
118     rep (i, 1, n) {
119         scanf("%d %d", &r[i].w, &r[i].h);
120     }
121     handle();
122     rep (i, 1, n) ans += r[i].w * r[i].h;
123     rep (i, 1, n) rep (j, 1, n) if(i != j && r[i].h >= r[j].h && r[i].w >= r[j].w) {
124         km.add(j - 1, i - 1, r[j].w * r[j].h);
125     }
126     ans -= km.solve(n, n);
127     cout << ans << endl;
128 }
129
130 int main()
131 {
132 //    FIN;
133     cin >> n;
134     solve();
135     return 0;
136 }
  1 #pragma comment(linker, "/STACK:1677721600")
  2 #include <map>
  3 #include <set>
  4 #include <stack>
  5 #include <queue>
  6 #include <cmath>
  7 #include <ctime>
  8 #include <bitset>
  9 #include <vector>
 10 #include <cstdio>
 11 #include <cctype>
 12 #include <cstring>
 13 #include <cstdlib>
 14 #include <iostream>
 15 #include <algorithm>
 16 using namespace std;
 17 #define INF 0x3f3f3f3f
 18 #define inf (-((LL)1<<40))
 19 #define lson k<<1, L, (L + R)>>1
 20 #define rson k<<1|1,  ((L + R)>>1) + 1, R
 21 #define mem0(a) memset(a,0,sizeof(a))
 22 #define mem1(a) memset(a,-1,sizeof(a))
 23 #define mem(a, b) memset(a, b, sizeof(a))
 24 #define FIN freopen("in.txt", "r", stdin)
 25 #define FOUT freopen("out.txt", "w", stdout)
 26 #define rep(i, a, b) for(int i = a; i <= b; i ++)
 27 #define dec(i, a, b) for(int i = a; i >= b; i --)
 28
 29 //typedef __int64 LL;
 30 typedef long long LL;
 31 typedef pair<int, int> Pair;
 32 const int MAXN = 800 + 10;
 33 const int MAXM = 110000;
 34 const double eps = 1e-12;
 35 LL MOD = 1000000007;
 36
 37 //以下是使用邻接表存边,不是使用vector,某些时候比上述要稍快一下
 38 /*******************************************************************/
 39 struct Edge {
 40     int to, cap, flow, cost, next;
 41     Edge(){}
 42     Edge(int _n, int _v, int _c, int _f, int _cost){
 43         next = _n; to = _v; cap = _c;
 44         flow = _f; cost = _cost;
 45     }
 46 };
 47
 48 struct MCMF
 49 {
 50     int n, m, src, des;
 51     int head[MAXN], tot;
 52     Edge edges[MAXM];
 53     int inq[MAXN];
 54     int d[MAXN];
 55     int p[MAXN];
 56     int a[MAXN];
 57
 58     void init(int n, int src, int des) {
 59         this->tot = 0;
 60         this->n = n;
 61         this->src = src;
 62         this->des = des;
 63         mem1(head);
 64     }
 65
 66     void add_edge(int from, int to, int cap, int cost) {
 67         edges[tot] = Edge(head[from], to, cap, 0, cost);
 68         head[from] = tot ++;
 69         edges[tot] = Edge(head[to], from, 0, 0, -cost);
 70         head[to] = tot ++;
 71     }
 72
 73     bool bellman_ford(int s, int t, int& flow, int& cost) {
 74         for(int i = 0; i < n; i ++) {
 75             d[i] = INF;
 76             inq[i] = 0;
 77         }
 78         d[s] = 0; inq[s] = 1;
 79         p[s] = 0; a[s] = INF;
 80
 81         queue<int>Q;
 82         Q.push(s);
 83         while(!Q.empty()) {
 84             int u = Q.front(); Q.pop();
 85             inq[u] = false;
 86             for(int i = head[u]; i != -1; i = edges[i].next) {
 87                 int v = edges[i].to;
 88                 if(edges[i].cap > edges[i].flow && d[v] > d[u] + edges[i].cost) {
 89                     d[v] = d[u] + edges[i].cost;
 90                     p[v] = i;
 91                     a[v] = min(a[u], edges[i].cap - edges[i].flow);
 92                     if(!inq[v]) {
 93                         Q.push(v);
 94                         inq[v] = 1;
 95                     }
 96                 }
 97             }
 98         }
 99         if(d[t] >= 0) return false;
100
101         flow += a[t];
102         cost += d[t] * a[t];
103
104         int u = t;
105         while(u != s) {
106             edges[p[u]].flow += a[t];
107             edges[p[u]^1].flow -= a[t];
108             u = edges[p[u]^1].to;
109         }
110         return true;
111     }
112
113     int min_cost() {
114         int flow = 0, cost = 0;
115         while(bellman_ford(src, des, flow, cost));
116         return cost;
117     }
118
119 }mcmf;
120 /***************************************************************/
121
122
123 struct Node {
124     int w, h;
125     bool operator < (const Node &A) const {
126         if(w != A.w) return w < A.w;
127         return h < A.h;
128     }
129     bool operator == (const Node &A) const {
130         return w == A.w && h == A.h;
131     }
132 }r[MAXN];
133 int n;
134
135
136 void handle() {
137     sort(r + 1, r + n + 1);
138     int cnt = 1;
139     rep (i, 2, n) if(!(r[i] == r[i - 1])) {
140         r[++cnt] = r[i];
141     }
142     n = cnt;
143 }
144
145 int solve() {
146     int ans = 0;
147     rep (i, 1, n) {
148         scanf("%d %d", &r[i].w, &r[i].h);
149     }
150     handle();
151     mcmf.init(2 * (n + 1), 0, 2 * n + 1);
152     rep (i, 1, n) {
153         ans += r[i].w * r[i].h;
154         mcmf.add_edge(mcmf.src, 2 * i - 1, 1, 0);
155         mcmf.add_edge(2 * i, mcmf.des, 1, 0);
156     }
157     rep (i, 1, n) rep (j, 1, n) if(i != j && r[i].h >= r[j].h && r[i].w >= r[j].w) {
158         mcmf.add_edge(2 * j - 1, 2 * i, 1, -r[j].w * r[j].h);
159     }
160     ans += mcmf.min_cost();
161     cout << ans << endl;
162 }
163
164 int main()
165 {
166 //    FIN;
167     cin >> n;
168     solve();
169     return 0;
170 }
时间: 2024-11-03 03:34:26

51nod 算法马拉松4 D装盒子(网络流 / 二分图最优匹配)的相关文章

网络流——二分图最优匹配KM算法

前言 其实这个东西只是为了把网络流的内容凑齐而写的(反正我是没有看到过这样子的字体不知道田忌赛马算不算) 算法过程 我们令左边的点(其实二分图没有什么左右)为女生,右边的点为男生,那么: 为每一个女生定一个心仪值,心仪值为她与男生连边中的最大值 为每一个女生找对象,要求男生的心仪值和女生的心仪值的和为他们的边权(男生的心仪值初始为0真惨) 如果没有找到对象,那么将2过程中的女生的心仪值全部-Min,男生的心仪值全部+Min(这个Min是通过自己算的,就是女生除了之前或当前心仪的男生外最心仪的男生

随便玩玩系列之一:SPOJ-RNG+51nod 算法马拉松17F+51nod 1034 骨牌覆盖v3

先说说前面的SPOJ-RNG吧,题意就是给n个数,x1,x2,...,xn 每次可以生成[-x1,x1]范围的浮点数,把n次这种操作生成的数之和加起来,为s,求s在[A,B]内的概率 连续形的概率,想象为一个n维长方体,有两个平面与这个几何图形相割,于是就变成了求面(体)积问题,一般要去重,n维区域系数:s^n/n!,至于区间问题,直接前缀之差搞定 然后就是悲催的算法马拉松17F题了...其实是道好题来的,只是出题人不知世界上还有这题,然后某大牛把思路理清后把答案直接搬了过来 经典的1*2骨牌覆

51nod 算法马拉松4

http://www.51nod.com/contest/problemList.html#!contestId=9 D 装盒子 拓扑排序?

51Nod 算法马拉松21(迎新年)

这次打算法马拉松是在星期五的晚上,发挥还算正常(废话,剩下的题都不会= =). 讲讲比赛经过吧. 8:00准时发题,拿到之后第一时间开始读. A配对,看上去像是二分图最大权匹配,一看范围吓傻了,先跳过读后面的题. B完全二叉树的方差,大概看了一遍,好神的样子,跳过. C多项式?好吧没学过FFT和NTT的我肯定不会,跳跳跳. D最大值,哎呦这函数什么破玩意儿,看不懂,跳跳跳. E B君的射击,卧槽毕克大人您出题就算了出这么一道码农题是要闹那样,跳跳跳. F那些年,我们一起讲的故事,卧槽这特么简直就

51Nod 算法马拉松15 记一次悲壮而又开心的骗分比赛

OwO 故事的起源大概是zcg前天发现51Nod晚上有场马拉松,然后他就很开心的过去打了 神奇的故事就开始了: 晚上的时候我当时貌似正在写线段树?然后看见zcg一脸激动告诉我第一题有九个点直接输出B就可以A.. 然后之后zcg以奇怪的二分方式发现了如何A掉第一题的第十个点(我记得貌似是什么第5000个数等于511? OwO 就这样没有任何思考含量全凭骗分黑科技过掉了第一题 OwO 然后zcg打开了第二题,发现第二题样例有点问题,然后就发了个帖子,直接去看第三题了 我去瞅了一眼,发现这不是gcd

51nod算法马拉松 contest7

A题 链接:http://www.51nod.com/contest/problem.html#!problemId=1417 推荐链接:http://blog.csdn.net/a837199685/article/details/45009337 设美女取得正面概率是p,反面就是(1-p),就是美女取一正一反和一反一正的概率相同,然后推出公式y=((a+b)/2+b)/(2*(a+b)); 1 #include<iostream> 2 #include<cstdio> 3 #i

51Nod 算法马拉松23 开黑记

惨啊--虽然开了半天黑,但是还是被dalao们踩了-- 第二次开黑,还是被卡在rank20了,我好菜啊--= = 写一写比赛经过吧-- 看到题之后习惯性都打开,A~D看上去似乎并没有什么思路,F应该是道数论题,看了E感觉有点意思,一看数据范围,咦怎么只有$50000$,再仔细看一看式子,手动分情况讨论之后得到一个结论-- 这题是水的线段树维护莫队啊= = 然后就开始码码码,由于一些脑残错误调了一会儿,然后就得到了这样的结果: 我坚信自己莫队的复杂度没错,然后就开始各种王逸松卡常,使用的卡常技巧包

51NOD算法马拉松 最大值问题 离线预处理+set lower_bound

题目:http://www.51nod.com/contest/problem.html#!problemId=1349 题意:100000个数的序列,有100000次询问,每次问区间最大值大于等于k的区间有多少? 思路:一开始没看到"大于等于",想了很久也不会,原来看错题了.看错题害死人. 一般询问的问题,如果不能用线段树log(n)求出,那么就离线做. 首先将询问按从大到小排序,再将序列中的每个数排序,注意记录序号. 对于当前询问,每加进一个数,我需要找到它在加进的序列(按大小有序

51nod算法马拉松13

A 取余最长路 不难发现路径可以拆成三条线段,只要知道两个转折点的位置就能计算出答案. 设sum(i,l,r)表示第i行从l到r元素的和,则答案可以表示为sum(1,1,x)+sum(2,x,y)+sum(3,y,n)%p. 前缀和一下转化成(S3[n]-S3[y-1])+S2[y]+(S1[x]-S2[x-1])%p,从小到大枚举y,将所有(S1[x]-S2[x-1])扔到一个集合里,用个set就能轻松实现了. 时间复杂度为O(NlogN). #include<cstdio> #includ