E 小红的二分图构造
先思考如何把 判掉。
- 由于一条边贡献
的度,所以所有点的度数和必须为偶数。
接下来考虑如何构造。我们可以直接按照二分图的定义来,把原来的所有点分成两个集合,保证两个集合的度数和相等即可。
那么现在问题就变成了如何分集合,注意到这是一个经典01背包,但是需要知道具体的方案,所以需要进行转移的记录。
时间复杂度
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
const int INF = 0x3f3f3f3f;
int d[N];
int dp[N][N * N];
// dp[i][j] 表示前i个点中,A集合的度数和为j,A集合中的点数
int main() {
int n; cin >> n;
int sum = 0;
for (int i = 1; i <= n; i ++ ) {
cin >> d[i];
sum += d[i];
}
if (sum & 1) {
cout << -1 << endl;
return 0;
}
for (int i = 0; i <= n; i ++ ) {
for (int j = 0; j <= sum / 2; j ++ ) dp[i][j] = -INF;
}
dp[0][0] = 0;
for (int i = 1; i <= n; i ++ ) {
for (int j = 0; j <= sum / 2; j ++ ) {
// 不选i
dp[i][j] = dp[i - 1][j];
// 选i
if (j >= d[i]) dp[i][j] = max(dp[i][j], dp[i - 1][j - d[i]] + 1);
}
}
if (dp[n][sum / 2] <= 0) {
cout << -1 << endl;
return 0;
}
// 分组
int ni = n, nj = sum / 2;
vector<int> v1, v2;
while (ni > 0) {
if (dp[ni][nj] == dp[ni - 1][nj]) {
// 没选i,分到2组
v2.push_back(ni);
ni --;
} else {
// 选了i,分到1组
v1.push_back(ni);
nj -= d[ni];
ni --;
}
}
cout << sum / 2 << endl;
int n1 = v1.size();
int n2 = v2.size();
for (int i = 0, j = 0; i < n1 && j < n2;) {
cout << v1[i] << ' ' << v2[j] << endl;
d[v1[i]] --, d[v2[j]] --;
if (d[v1[i]] == 0) i ++;
if (d[v2[j]] == 0) j ++;
}
return 0;
}