51nod 算法马拉松4 D装盒子(网络流 / 二分图最优匹配)
有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 }