HDU 5506(暴力角度题目)

题意:

给定最多30个集合让划分成L个部分(每部分由若干完整集合组成,L<=min(5,n)),每个集合内最多10个元素,元素都是小于300的正整数,问给定一组集合能不能划分成

L部分且每个部分的集合最少有一个公共元素。

分析:

这样的题目用确定一个基本点的方法,进行枚举,首先1集合必定在L个集合中的一个,那么可以直接限定它出现在第一个集合,那么枚举10个元素到底哪一个值是公共的,然后标记所有有这个元素的集合,再找到第一没有这个元素的集合继续如此,如果递归<=L层,就覆盖了所有集合,那么一定存在可行解,否则当前方案不合法。

这样的枚举是高效的最多5层,每层10个分支,转移为o(n),这样的话复杂度最坏为n*1e5。

#include <cstring>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
typedef unsigned long long llu;
#define rep1(i,x,y) for(int i=x;i<=y;i++)
#define rep(i,n) for(int i=0;i<(int)n;i++)

const int N = 33;
int n,L;
int deg[N],ah[N][330],a[N][N],vis[N];
bool flag;
void dfs(int p,int hav ,int cnt){
   if(flag) return ;
   if(hav == L) return ;
   int ok = 0;
   rep1(j,1,deg[p]){
       int v = a[p][j];
       vector<int> cc;
       rep1(k,j,n) if(!vis[k] && ah[k][v]) cc.push_back(k),vis[k]=1;//,cout<<k<<"** ";
       int nexp = -1;
       rep1(k,j,n) if(!vis[k]) {nexp = k; break;}
       if(nexp == -1) {flag = true; return ;}
       dfs(nexp,hav+1,cnt+cc.size());
       rep(k,cc.size()) vis[cc[k]] = 0;
   }
}
int main()
{
   int T;
   scanf("%d",&T);
   while(T--){
       scanf("%d %d",&n,&L);
       memset(ah,0,sizeof(ah));
       rep1(i,1,n) {
          scanf("%d",°[i]);
          rep1(j,1,deg[i]) scanf("%d",&a[i][j]),ah[i][a[i][j]]=1;
       }
       flag = false;
       memset(vis,0,sizeof(vis));
       dfs(1 , 0 , 0);
       printf("%s\n",flag ? "YES":"NO");
   }
   return 0;
}


你可能感兴趣的:(HDU 5506(暴力角度题目))