BZOJ 3144 HNOI 2013 切糕
2016-01-17 22:55
323 查看
题解
首先我们不考虑其它,如果这个切糕只有1∗1∗r,我们的线性规划式可以表示成:min{∑max{0,xi−xi−1}⋅vi+∑max{0,1−xn}⋅∞}
令 x0=0,即源点。
这样子可以限制数列{vn}只取1个点且数最小。只会允许 {xn}前段全为0,后段全为1。
取0意味着割点在i之后,因为i∈S;1意味着割点在i之前或就是i本身,因为i∈T。
就这个式子本身而言,如果更加直观,可以这么写:
min{∑max{0,xi−x′i}⋅vi+∑max{0,x′j−xi}⋅∞}
其中x′n+1=1,x0=0。效果是一样的,无非就是拆个点。
其实从构好的图也可以看出,一列点依次有向的连接,最后连到汇点的是inf的边,那么最小割只会取最小的。
那么现在扩充到p∗q∗r,就是扩展一下式子:
min{∑max{0,xi,j,k−xi,j,k−1}⋅vi+∑max{0,1−xi,j,r}⋅∞}
式子仍不满足要求,因为存在∣∣f(x,y)−f(x′,y′)∣∣≤D
所以我们继续扩展式子:
min{∑max{0,xi,j,k−xi,j,k−1}⋅vi+∑max{0,1−xi,j,r}⋅∞+∑max{0,xi′,j′,k−d−xi,j,k}⋅∞}
其中∣∣x−x′∣∣+∣∣y−y′∣∣=1
分情况讨论,如果xi′,j′,k−d为1,那么说明(i′,j′)列被割的点在k−d之前,xi,j,k取0是不合法的,因为(i,j)列的割点在k之后,显然距离超过D,画图可知。
如果xi′,j′,k−d为0,那么说明(i′,j′)列被割的点在k−d之后,xi,j,k取1和0对于xi,j,k本身是合法的。而如果xi′,j′,k−d取值0不合法,会有xi,j,k后面的点判断不合法。
因此我们可以构图。
s连向(i,j,1),边权v(i,j,1);(i,j,k−1)连向(i,j,k),边权v(i,j,k);(i,j,r)连向t,边权∞;(i,j,k)连向(i′,j′,k−d),边权∞。
跑一下网络流即可。
想法比别人复杂好多。
实际上这种题用不上线性规划构图。。
实际上这个构图很直观就可以yy出来了。
直观构图可以看http://www.cnblogs.com/zig-zag/archive/2013/05/13/3076563.html 和http://blog.csdn.net/zarxdy34/article/details/45272055。
88ms真慢。。
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; const int N = 100005, M = N * 5, inf = 0x3f3f3f3f; namespace Graph { int head , next[M], to[M], vis , cur , level , que[N * 10], w[M]; ; int edge_cnt = 0, s, t; void add(int a, int b, int c) { next[edge_cnt] = head[a]; to[edge_cnt] = b; w[edge_cnt] = c; head[a] = edge_cnt++; next[edge_cnt] = head[b]; to[edge_cnt] = a; w[edge_cnt] = 0; head[b] = edge_cnt++; } bool bfs() { memset(level, -1, sizeof level); int f = 0, r = 0; que[r++] = s; level[s] = 0; while (f < r) { int u = que[f++]; for (int i = head[u]; i != -1; i = next[i]) if (w[i] > 0 && level[to[i]] == -1) { level[to[i]] = level[u] + 1; que[r++] = to[i]; } } return level[t] != -1; } int dfs(int u, int low) { if (u == t) return low; int res = 0; for (int i = cur[u]; i != -1 && res < low; i = next[i]) if (w[i] > 0 && level[to[i]] == level[u] + 1) { int tmp = dfs(to[i], min(low - res, w[i])); res += tmp; w[i] -= tmp; w[i ^ 1] += tmp; if (w[i]) cur[u] = i; } if (!res) level[u] = -1; return res; } int solve() { int ans = 0; while (bfs()) { memcpy(cur, head, sizeof cur); ans += dfs(s, inf); } return ans; } } int dx[] = {0, 0, 1, -1}; int dy[] = {-1, 1, 0, 0}; int v[41][41][41]; int main() { using namespace Graph; int p, q, r, d, i, j, k, l; memset(head, -1, sizeof head); scanf("%d%d%d%d", &p, &q, &r, &d); for (i = 1; i <= r; i++) for (j = 1; j <= p; j++) for (k = 1; k <= q; k++) scanf("%d", &v[j][k][i]); s = 0; t = p * q * r + 1; #define id(x,y,z) ((z==0)?0:((z-1)*p*q+(x-1)*q+y)) for (i = 1; i <= p; i++) for (j = 1; j <= q; j++) { for (k = 1; k <= r; k++) { add(id(i, j, k - 1), id(i, j, k), v[i][j][k]); if (k > d) for (l = 0; l < 4; l++) { int nx = i + dx[l], ny = j + dy[l]; if (nx < 1 || nx > p || ny < 1 || ny > q) continue; add(id(i, j, k), id(nx, ny, k - d), inf); } } add(id(i, j, r), t, inf); } printf("%d", solve()); return 0; }
3144: [Hnoi2013]切糕
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1039 Solved: 592
Description
Input
第一行是三个正整数P,Q,R,表示切糕的长P、 宽Q、高R。第二行有一个非负整数D,表示光滑性要求。接下来是R个P行Q列的矩阵,第z个 矩阵的第x行第y列是v(x,y,z) (1≤x≤P, 1≤y≤Q, 1≤z≤R)。100%的数据满足P,Q,R≤40,0≤D≤R,且给出的所有的不和谐值不超过1000。
Output
仅包含一个整数,表示在合法基础上最小的总不和谐值。Sample Input
2 2 21
6 1
6 1
2 6
2 6
Sample Output
6HINT
最佳切面的f为f(1,1)=f(2,1)=2,f(1,2)=f(2,2)=1相关文章推荐
- Windows 2008 R2防火墙,允许被ping
- Java的身份证号码工具类
- 中文乱码?不,是 HTML 实体编码!
- [LeetCode] 204. Count Primes 解题思路
- android的IP拨号器
- 蓝牙(BLE)应用框架接口设计和应用开发——以TI CC2541为例
- JAVA线程池全解
- UESTC 1271 Search gold (DP)
- 编程题#4: 字符串操作(C++程序设计第9周)
- Swift初体验
- 又到招聘季,聊点寻龙诀
- [置顶] Servlet深入学习,规范,理解和实现(上)
- 【C】我到底错在那里………………
- 搜索引擎常用技巧
- #STM32# 学习日志-第4天
- ffmpeg最简单的解码保存YUV数据
- CAA 控制台窗口的输出
- 【C】我到底错在那里………………
- CSS基础-插曲
- MVC 中@Html.DropDownListFor() 设置选中项 这么不好使 ? [问题点数:40分,结帖人lkf181]