---
# System prepended metadata

title: '部分题解：[从入门到头秃周末休闲赛73 (F ~ I)](https://vjudge.net/contest/444713)'
tags: [题解, 算法]

---

---
tags: 题解,算法
---

# 部分题解：[从入门到头秃周末休闲赛73 (F ~ I)](https://vjudge.net/contest/444713)

## F 秤 **

对w集合进行DFS，找出所有能称出来的质量集合，然后对每个不能称出来的ai求一个需要添加的w质量的集合，最后求它们的交集的最小值。

### 参考代码（C++11）

```cpp=
#include <iostream>
#include <set>
#include <vector>

using namespace std;

void dfs(int w[], int len, int sum, set<int>& s) {
    if (len == 0) { s.insert(sum); return; }
    dfs(w, len - 1, sum, s);
    dfs(w, len - 1, sum + w[len - 1], s);
    dfs(w, len - 1, sum - w[len - 1], s);
}

int solve(int n, int a[], int m, int w[]) {
    set<int> s, r;
    dfs(w, m, 0, s);
    for (int i = 0; i < n; ++i)
        if (s.count(a[i])) swap(a[i--], a[--n]);
    if (n == 0) return 0;
    for (auto& it : s)
        r.insert(abs(it - a[0]));
    for (int i = 1; i < n; ++i) {
        set<int> t;
        for (auto& it : s) {
            int v = abs(it - a[i]);
            if (r.count(v))
                t.insert(v);
        }
        r = move(t);
    }
    if (r.empty()) return -1;
    return *r.begin();
}

int main() {
    int n, m;
    int a[200], w[20];
    while (cin >> n >> m && n + m > 0) {
        for (int i = 0; i < n; ++i) cin >> a[i];
        for (int i = 0; i < m; ++i) cin >> w[i];
        cout << solve(n, a, m, w) << endl;
    }
    return 0;
}
```

以上方法不是最优，换个迭代方式可以节省2/3时间。

## G 最大矩形 ***

### 方法1

设`'#'`表示1，`'.'`表示0的话，通过观察你会发现，这个最大矩形里，假设第一行是v0，那么接下来的几行，要么是v0，要么是~v0，同样，按列算也是一样。那么，我们穷举这个最大矩形的下边界，然后以此计算相邻的两列，最高有多高满足它们是完全相等或完全相反，这样就得到一个大小为w-1的高度数组，那就变成典型的单调栈题。本题也类似01矩阵里面找最大的全0矩阵的题目。

### 方法2

通过方法1的规律做出推论，把原矩阵里，共`(h-1)*(w-1)`个2x2小矩阵，我们就来生成这个新的`(h-1)*(w-1)`矩阵，如果这个小矩阵里面1的个数是奇数，那用1表示，否则用0表示，那么原题就变成在新矩阵里找最大的全0矩阵，套模板即可。

## H 困兽斗 ***

求出所有交点，并根据线段相连成若干个平面图，这些图之间没有交点。然后，对于每个平面图，扫描出它的包围多边形（度为1的点可以在生成平面图时删去），最后就转化成判断原点在不在多边形内的问题，解法是计算这个多边形有多少次穿越x正半轴，如果是奇数次，那么原点在多边形内，否则就在多边形外。如果原点在任一多边形内，输出yes，否则输出no就可以了。

## I 树上游戏 ****

本题网上到处都是题解，直接搜索或看[这个](https://blog.csdn.net/baidu_36797646/article/details/88571722)