C.Anadi and Domino
题目:https://codeforces.com/contest/1230/problem/C
题意:
有一堆上下都有数字的牌:
给你n(n<=7)个点 ,m条边(无重边,最多为 n*(n-1)/2) ,的图,在图上的边可以放这些牌,有两点限制:
1.每张牌只能用一次
2.对于每个点,它的边上的牌向着它的那半边牌上的点数要一样
题解:
首先很明显可以将向着某个点的点数代替这个点
然鹅看到n<=7我就开始无脑暴力,枚举所有点的情况,找出结果最大的。(我果然无脑。。。)
其实仔细看n和m的数据,m最多也就让每个点两两连边
假如n<=6的话,此时两个点之间必定可以找到一张牌符合条件的,答案就是m
假如m=7的话,就需要找到两个点合并(这两个点的点数一样),只需要枚举完所有的情况,计算答案即可
#include<bits/stdc++.h>
using namespace std;
const int maxn = 9;
int deg[maxn];
int ma[maxn][maxn];
int main() {
int n, m;
cin >> n >> m;
if (m == 0) {
cout << 0;
return 0;
}
if (n <= 6) cout << m;
else {
int fro, to;
for (int i = 1; i <= m; i++) { //建边
scanf("%d %d", &fro, &to);
ma[fro][to] = ma[to][fro] = 1;
}
int ans = 0;
for (int i = 1; i <= n; i++) {//枚举所有两个点合并的情况,i为第i个点
for (int j = i+1; j <= n; j++) { //i 和 j合并
int res = m;
//合并之后两个点的边归到同一个点,此时假如有重复的边需要删掉
for (int k = 1; k <= n; k++) {
if (ma[i][k] && ma[j][k]) res--; //删掉重复的边
}
ans = max(ans, res);
}
}
cout << ans << endl;
}
}
D.Marcin and Training Camp
题目:https://codeforces.com/contest/1230/problem/D
题意:
教练选人,每个人有a,b两个值,b表示这个人的能力,a表示这个人的技能点(以二进制方式储存,1表示有该技能,0表示没有)
一个人要好好融入团队工作需要他觉得他并没有比这个团队的每一个人牛逼
一个人觉得他比另外一个人牛逼的条件就是他存在另外一个人没有的技能点
所以两个人可以相互觉得自己比对方牛逼
教练想让团队的所有人都能好好融入团队,并且能力总和最大,注意团队人数>=2 ,否则输出0
问团队能力总和最大是多大
(这题很容易想着想着就想歪了啊orz。。。)
在一个团队中必然存在两个或者以上技能点完全一样的人,这个是关键
因为假如一个团队中每个人的技能点都不一样
看图:
(这些都是二进制表示的技能点)
总不会达到平衡
所以先对a值相同的合并
然后对于每一个合并完之后的值都去找其覆盖(覆盖指能力完全覆盖)
比如:
对于 110 这个能力,其覆盖就是: 000 100 010 110
合并完的值+其覆盖值 = 一个其乐融融的团队
不同的其乐融融的团队都可以融合成一个更大的其乐融融团队
所以最后输出最大的其乐融融团队
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 7007;
struct node {
ll a, b;
bool operator <(node e) {
return a < e.a;
}
}num[maxn];
ll val[maxn];
int n;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) scanf("%I64d", &num[i].a);
for (int i = 1; i <= n; i++) scanf("%I64d", &num[i].b);
sort(num + 1, num + 1 + n); //排序然后合并
int t=0;
int flag = 0;
ll ans = 0;
for (int i = 2; i <= n; i++) {//合并
if (num[i].a == num[i - 1].a) {
if (flag == 0) {
flag = 1;
val[++t] = num[i].a;
}
}
else flag = 0;
}
for (int i = 1; i <= t; i++) { //这个是合并的点
for (int j = 1; j <= n; j++) { //对于每一个合并之后的点去找其覆盖
if ((val[i] | num[j].a) == val[i]) {
ans += num[j].b; //找到就加上
num[j].b = 0; //一个点贡献只算一次
}
}
}
cout << ans << endl;
}