AcWing 1118. 分成互质组
考察:玄学dfs
思路1:
枚举组,判定当前数能否放进当前所有组.if 能, 加入到组中 else 新创立一个组.关键在于判定组内元素与当前数是否互质,这里用乘积代表组内的元素,如果与乘积不会互质,那么与组内所有元素一定不互质.(但是感觉会爆long long,10个数字,1040,但是实际测试没有爆)
时间22ms
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <vector> 5 using namespace std; 6 typedef long long LL; 7 const int N = 13,INF = 0x3f3f3f3f; 8 int a[N],ans = INF,n; 9 vector<LL> v; 10 int gcd(int a,int b) 11 { 12 return b?gcd(b,a%b):a; 13 } 14 void dfs(int x) 15 { 16 if(v.size()>=ans) return; 17 if(x>n) 18 { 19 ans = min(ans,(int)v.size()); 20 return; 21 } 22 for(int i=0;i<v.size();i++) 23 { 24 if(gcd(a[x],v[i])==1) 25 { 26 v[i] = v[i]*a[x]; 27 dfs(x+1); 28 v[i] = v[i]/a[x]; 29 } 30 } 31 v.push_back(a[x]); 32 dfs(x+1); 33 v.pop_back(); 34 } 35 int main() 36 { 37 scanf("%d",&n); 38 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 39 dfs(1); 40 printf("%d\n",ans); 41 return 0; 42 }
思路2:
对于当前组,枚举每一个能放进去的数.如果没有能放进去的数,说明我们要开一个新组.
这种搜索可以搜到最优解.对于当前数x,假设它可以放入当前组C,但是设它放在新组能获得最优解.放完所有元素后,x能在新组中被拿出,再放入第C组,不影响最优解大小.
这里需要两个剪枝: 如果当前枚举组数比最优解大 return,如果当前组可以放入元素,就不需要枚举它不放元素的情况.
还有一个重要的优化是这里放组是组合型放入,也就是不是排列型枚举,可以用一个变量start规定按下标枚举.只放入比当前数字下标大的元素.
这种搜索实际上枚举了每组从每个元素开始放的情况.有大佬在另开新组的地方优化为新租放第一个还未没分组的数,代码优化到40ms..
时间700ms
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int N = 11; 6 int a[N],n,g[N][N],ans = N; 7 bool st[N]; 8 int gcd(int a,int b) 9 { 10 return b?gcd(b,a%b):a; 11 } 12 bool check(int gr,int sz,int x) 13 { 14 for(int i=1;i<=sz;i++) 15 if(gcd(g[gr][i],x)>1) return 0; 16 return 1; 17 } 18 void dfs(int gr,int sz,int start,int tot) 19 { 20 if(gr>=ans) return;//最优性剪枝. 21 if(tot==n) {ans = gr;return;} 22 bool ok = 0;//如果能放元素就尽量不要放新组. 23 for(int i=start;i<=n;i++) 24 { 25 if(!st[i]&&check(gr,sz,a[i])) 26 { 27 st[i] = 1; 28 g[gr][sz+1] = a[i]; 29 dfs(gr,sz+1,i+1,tot+1); 30 ok = 1; 31 st[i] = 0; 32 } 33 } 34 if(!ok) dfs(gr+1,0,1,tot);//枚举的组已经不能再放元素. 35 } 36 int main() 37 { 38 scanf("%d",&n); 39 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 40 dfs(1,0,1,0); 41 printf("%d\n",ans); 42 return 0; 43 }