Unshuffle(深搜加剪枝-hdu4665)
【题目解法】:
一.深搜加剪枝:
为什么n=2000的2^2000种情况可以用深搜?
1)我想应该是这题的条件比较强,必须保证已选的序列必须前面的部分完全相等才行,这是一个很强的剪枝条件,应该不用等到构造完序列而很快就能把不符合的剪掉;
2)另外题目中说只要找到任何一组就行了,所以一旦找到符合的就直接结束DFS,因此不会全部遍历枚2^2000种情况,这也是一个有效的剪枝条件;
3)另外还可以预先将第一个元素分在第一组,这是等价性剪枝,因为结果与分的第几组无关,而只与得到的两个序列有关。
二.题解分析
做题时,当时被wa懵了,没有深入分析到题解这一层,我来解释下题解:
分析:由题意一种颜色要么出现2次,要么出现4次;
a.如果一个序列中所有颜色仅出现2次。
假设当前位置第一次出现颜色a,我优先给第一个序列,第二个颜色a的位置有两种情况:1.在第一个颜色a的下一个,那我直
接给第二个序列,然后就这样推下去;2.在两个a之间还存在其他颜色,那这些颜色中不可能出现两种相同的颜色,否则就不满足构成两个相同的序列,所以都是单色的,这些颜
色凡是在a前面的给第二个序列,这肯定是它少于第一个序列的那部分,凡是之前没出现过的全部给第一个序列,然后到达第二个颜色a位置,给第二个序列,问题又进入了下一
轮,所以这样选下去总是可以的;按照我刚开始的错误做法是可行的。
b.如果一个序列中有一个或多个颜色出现4次。
那我能不能还按上述过程推理了呢?不能,因为情况a中两种相同颜色间必然不存在相同的颜色,而情况b可嵌套好多种颜色。情况变得更为复杂,所以需从别的角
度着手去分析。
我来说下为甚么没有a和d配对的情况,如果存在上面四种相同的颜色a1,a2,a3,a4;那我可以证明各种情况下都可以由a1和a4配对,a2和a3配对而形成的序列都可以由a1和a3配对,a2和a4配对或a1和a2配对,a3和a4配对等价的得到相同的序列,所以这种情况可被代替,只用考虑题解中的两种情况就可以了。
【解题回顾】:
1)这题刚开始看了题目想当然的一位很简单,只要用两个数组依次记下相同的成分就行了,if(flag1[arr[i]]==flag2[arr[i]]) flag1[arr[i]]++;
Else flag2[arr[i]]++;
看似没有任何问题,可当过不了豪哥的一组测试数据后,才动摇了,其实还是对题目理解不深,仅仅是觉得模棱两可以做时,就赶紧敲代码,争取少的罚时,其实多花点时间深刻透彻的理解题意才是最重要的。
2)在接下来虽然理解更进一层,可还是有些地方理解错误,导致又陷入无可奈何之中,但还是豪哥的测试数据挽救了我们,可见出测试数据的能力也是ACMer必须要具备的能力。(下面会具体讲)
3)最终中与完明白了题意:题目看似两个序列都有序,其实放在一起是没法按照有序直接做的!当我完全理解题意后,问题也就变得更加复杂了,一时还真想不出有效的解法来,听别人说深搜加剪枝可以,深搜我想在这种情况下作为最无奈的选择也就姑且一试了,结果真的就险过了。
【源程序】
#include <iostream> #include <cstdio> #include <cstring> #include <queue> using namespace std; #define Max(a,b) (a)>(b)?(a):(b) #define Min(a,b) (a)<(b)?(a):(b) int arr[2010]; int tag,n; void dfs(char s[],int s1[],int p1,int s2[],int p2,int cur) { if(p1<=p2) { if(s1[p1]!=s2[p1]) return; } else { if(s1[p2]!=s2[p2]) return; } if(cur>n) { if(p1!=p2) return; else { for(int i=1;i<=n/2;i++) if(s1[i]!=s2[i]) return; tag=1; return; } } else { int temp=s[cur]; s[cur]='0'; s1[++p1]=arr[cur]; dfs(s,s1,p1,s2,p2,cur+1); if(tag==1) return; s[cur]=temp; --p1; s[cur]='1'; s2[++p2]=arr[cur]; dfs(s,s1,p1,s2,p2,cur+1); if(tag==1) return; s[cur]=temp; --p2; } } int main(){ //freopen("iofile\\input.txt","r",stdin); char s[2010]; int s1[2010],s2[2010]; int T,i; scanf("%d",&T); while(T--) { cin>>n; for(i=1;i<=n;i++) { cin>>arr[i]; } memset(s,0,sizeof(s)); memset(s1,0,sizeof(s1)); memset(s2,0,sizeof(s2)); tag=0; s1[0]=1; dfs(s,s1,1,s2,0,2); printf("%s\n",s+1); } return 0; }