Dividing a String
http://acm.hdu.edu.cn/showproblem.php?pid=4963
Problem Description
Let S be a string of length 2*N, where each character in S is assigned an integer as its weight. The weight of a subsequence T of S, denoted by weight(T), is defined as the sum of all its characters’ weights. Your task is to divide S into two subsequences T1 and T2, each of length N, such that:
1.T1 is equal to T2.
2.|weight(T1) – weight(T2)| is as small as possible.
1.T1 is equal to T2.
2.|weight(T1) – weight(T2)| is as small as possible.
Input
The input contains multiple test cases.
Each case consists of three lines.
The first line contains an integer N (1<=N<=20).
The second line is a string S of length 2*N. Each character in S is either ‘a’ or ‘b’. The third line contains 2*N positive integers, where the ith integer is the weight of the ith character in S. No integer exceeds 1000000.
The input is terminated by N = 0.
Each case consists of three lines.
The first line contains an integer N (1<=N<=20).
The second line is a string S of length 2*N. Each character in S is either ‘a’ or ‘b’. The third line contains 2*N positive integers, where the ith integer is the weight of the ith character in S. No integer exceeds 1000000.
The input is terminated by N = 0.
Output
For each case, output the smallest difference between weight(T1) and weight(T2) in a line. If it is impossible to divide S into two equal subsequence, output -1 instead.
Sample Input
2 abab 3 1 10 5 3 aaabbb 1 1 1 2 2 2 3 abaaba 4 6 5 10 3 4 0
Sample Output
11 -1 2
粗略的看了一下题解:
大致思路是:分别枚举前一半和后一半,枚举前一半时,设T1长度小于T2长度,且满足题意时,T1必定为T2的前缀,设T2比T1多出的串为C,复杂度为O(2^n);同理,枚举后一半时,设T1比T2多出的串为CC。则只有C=CC时,T1=T2,所以所有 C对应的sum减去CC对应的sum的绝对值的最小值即为答案
最开始没有排序,而知dfs_back每枚举出一个CC就在存好的C中遍历一边,导致TLE,再仔细看题解,发现只要排序后枚举相邻的两个C和CC即可,可以证明隔一个时不会比相邻的小。
改正过后又继续WA,最后随机了机组数据后发现,给短的字符串添加字符时,判断是否为前缀应该用num[i]==((c>>(j-k-1))&1),而开始写的是num[i]==((c&(1<<(j-k-1))),这个错在逆序dfs_back中的CC字符串时出现过,但及时改正了,这里没注意...以后要注意左右移的结果,不能想当然
为了方便,用二进制代表ab串,又由于01和001等无法区分,所以在串的最前面加一个1,即可区分所有长度不同的串
速度很快,2.5s左右,时间暂时排在第二 :),不过空间占用较大
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
struct Node {
int sum,type;
Node(int _sum,int _type):sum(_sum),type(_type) {}
bool operator< (const Node& a) const {
return sum<a.sum;
}
};
int ans,w[45],n,num[45],mn,mx;
int que[4200005],times=0;
vector<Node> weight[2100005];
void dfs_front(int i,int j,int k,int sumT1,int sumT2,int c) {//枚举前一半字符串
if(i>=n) {//前一半已遍历完
if(j<=k) {//|T1|<=|T2|
if(weight[c].size()==0)
que[times++]=c;
weight[c].push_back(Node(sumT2-sumT1,0));
mx=max(mx,c);
mn=min(mn,c);
}
return ;
}
if(j<k) {
if(num[i]==((c>>(k-j-1)&1)))//如果T1加上字符num[i]后仍是T2的前缀
dfs_front(i+1,j+1,k,sumT1+w[i],sumT2,(c&(~(1<<(k-j))))|(1<<(k-j-1)));//当前字符给T1
dfs_front(i+1,j,k+1,sumT1,sumT2+w[i],(c<<1)|num[i]);//当前字符给T2
}
else if(j>k){
dfs_front(i+1,j+1,k,sumT1+w[i],sumT2,(c<<1)|num[i]);
if(num[i]==((c>>(j-k-1))&1))//如果T2加上字符num[i]后仍是T1的前缀
dfs_front(i+1,j,k+1,sumT1,sumT2+w[i],(c&(~(1<<(j-k))))|(1<<(j-k-1)));
}
else {
dfs_front(i+1,j+1,k,sumT1+w[i],sumT2,(c<<1)|num[i]);
dfs_front(i+1,j,k+1,sumT1,sumT2+w[i],(c<<1)|num[i]);
}
}
void dfs_back(int i,int j,int k,int sumT1,int sumT2,int c) {//枚举后一半字符串
if(i<n) {
if(j>=k) {//令|T1|>=|T2|
int l=j-k-1,r=0;
while(l>r) {//由于是从后开始统计,所以字符串与从前统计的方向相反,需要反向
if(((c>>l)&1)!=((c>>r)&1)) {//当这两个字符不同时才互换(异或实现)
c^=1<<l;
c^=1<<r;
}
--l;
++r;
}
if(weight[c].size()==0)
que[times++]=c;
weight[c].push_back(Node(sumT1-sumT2,1));
mx=max(mx,c);
mn=min(mn,c);
}
return ;
}
if(j<k) {
if(num[i]==((c>>(k-j-1)&1)))//如果T1加上字符num[i]后仍是T2的后缀
dfs_back(i-1,j+1,k,sumT1+w[i],sumT2,(c&(~(1<<(k-j))))|(1<<(k-j-1)));//当前字符给T1
dfs_back(i-1,j,k+1,sumT1,sumT2+w[i],(c<<1)|num[i]);//当前字符给T2
}
else if(j>k){
dfs_back(i-1,j+1,k,sumT1+w[i],sumT2,(c<<1)|num[i]);
if(num[i]==((c>>(j-k-1))&1))//如果T2加上字符num[i]后仍是T1的后缀
dfs_back(i-1,j,k+1,sumT1,sumT2+w[i],(c&(~(1<<(j-k))))|(1<<(j-k-1)));
}
else {
dfs_back(i-1,j+1,k,sumT1+w[i],sumT2,(c<<1)|num[i]);
dfs_back(i-1,j,k+1,sumT1,sumT2+w[i],(c<<1)|num[i]);
}
}
int main() {
//freopen("in.txt","r",stdin);
int cnt[3];
char s[45];
while(scanf("%d",&n),n!=0) {
while(times>0)
weight[que[--times]].clear();
cnt[0]=cnt[1]=0;
scanf("%s",s);
for(int i=0;i<(n<<1);++i) {
scanf("%d",w+i);
num[i]=s[i]-'a';//将ab字符串处理成01串
++cnt[num[i]];//统计0和1出现的次数
}
if(((cnt[0]&1)==1)||((cnt[1]&1)==1)) {
printf("-1\n");
continue;
}
mx=0;
mn=21000005;
ans=0x3f3f3f3f;
dfs_front(0,0,0,0,0,1);
dfs_back((n<<1)-1,0,0,0,0,1);
for(int c=mn;c<=mx;++c) {
if(weight[c].size()>1) {
sort(weight[c].begin(),weight[c].end());
int j=-1,k=-1;
for(int i=0;i<weight[c].size();++i) {
if(weight[c][i].type==0)
j=i;
else
k=i;
if(j!=-1&&k!=-1)
ans=min(ans,abs(weight[c][j].sum-weight[c][k].sum));
}
}
}
printf("%d\n",ans==0x3f3f3f3f?-1:ans);
}
return 0;
}