Problem Description
输入n, k代表有n个问题,k个队伍。接下来输入一个n*k的矩阵,如果为1代表第j个队伍知道第i个题如何做,0代表不知道如何做。你想要找到一个关于这n道题的非空子集,每个参加队伍知道的题目不超过总数的一半。问是否能找到这么个子集。
思路:
如果存在一道题全部队伍都不知道,那么yes,否则就得找两道题,如果为yes,最坏情况就是每个队伍都知道一道题。如果两道题找不到,三道题,四道题…就不可能找得到。
所以核心点在于找两道题。我一开始想到的思路枚举任意两道题复杂度O(n^2),当然超时了,过程中也想到状态压缩,但是不知道如何优化时间。
比赛结束后,参考了别人的思路。如果我们枚举题目数量复杂度需要O(n^2),如果我们换换思路将每个题目包含的信息压缩成一个数,用数组下标标记一下。这样我们可以枚举一遍题目,对于每个题目暴力跑所有情况0 - (2^k)看看是否存在两个题目,所有队伍满足,再看看暴力跑的这个情况是否存在,复杂度降为(n*(2^k))。
最后我们还可以优化,枚举一遍题目,里面有很多题目情况是一致的,情况一致都不需要重复跑,所以我们可以枚举所有情况,在这个情况存在的情况下在枚举所有情况,如果两种情况满足条件,在看看另一种情况是否存在,如果存在输出yes,否则继续找。复杂度降为((2^k)^2)。
#include<bits/stdc++.h>
using namespace std;
int vis[100];
int main()
{
int n, k, v, i, num, j;
while(~scanf("%d %d", &n, &k))
{
memset(vis, 0, sizeof(vis));
while(n--)
{
v = 0;
for(i = 0; i < k; i++)
{
scanf("%d", &num);
if(num) v |= 1<<i;//状态压缩
}
vis[v] = 1;//存起来
}
for(i = 0; i < (1<<k) - 1; i++)//枚举所有情况
{
if(vis[i])//情况存在
{
for(j = 0; j < (1<<k) - 1; j++)//枚举所有情况
{
if(vis[j] && !(j&i)) break;//满足条件
}
if(j < (1<<k) - 1) break;//满足条件
}
}
if(i < (1<<k) - 1) printf("YES\n");//满足条件
else printf("NO\n");
}
}

本文探讨了一种竞赛编程中寻找特定子集的问题解决策略。通过状态压缩技术优化算法效率,实现从O(n^2)到O((2^k)^2)的时间复杂度降低。
632

被折叠的 条评论
为什么被折叠?



