HDU-4963 Dividing a String(列挙[途中出会い法])
Dividing a String
http://acm.hdu.edu.cn/showproblem.php?pid=4963 Time Limit: 30000/15000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
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.
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.
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
Sample Output
問題解をざっと見た.
大まかな考え方は、前半と後半をそれぞれ列挙し、前半を列挙する場合、T 1の長さがT 2の長さより小さく、かつ題意を満たす場合、T 1は必ずT 2の接頭辞であり、T 2がT 1より多く出た列をCとし、複雑度をO(2^n)とする.同様に,後半を列挙する場合,T 1がT 2より多く出た列をCCとする.C=CCのみの場合、T 1=T 2となるので、全てのCに対応するsumからCCに対応するsumの絶対値を減算した最小値が解答となる
最初はソートされていませんが、dfs_がわかります.backは1枚につき1つのCCを挙げて保存したCの中で片側を遍歴し,TLEを招き,さらに問題解をよく見ると,並べ替えた後に隣接する2つのCとCCを列挙すれば,1つ隔てた場合に隣接するものより小さくないことが証明される.
修正後もWAを続け、最後にユニットデータをランダムにしたところ、短い文字列に文字を追加した場合、接頭辞にnum[i]==((c>(j-k-1)&1)を使うべきかどうかを判断し、num[i]=((c&(1<(j-k-1))を書き始めた.あ、この間違いは逆順dfs_backのCC文字列の時に出たことがありますが、タイムリーに修正しました、ここでは注意していません…あとは左右に移動した結果に注意して、当然とは思えません
便宜上ab列をバイナリで表し,また01や001などは区別できないため,列の一番前に1を加えるとすべての長さの異なる列を区別できる.
速度は速くて、2.5 sぐらいで、時間は一時的に2番目に並んでいます:)しかし、空間の占有量はわりに大きいです
http://acm.hdu.edu.cn/showproblem.php?pid=4963 Time Limit: 30000/15000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
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.
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.
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
問題解をざっと見た.
大まかな考え方は、前半と後半をそれぞれ列挙し、前半を列挙する場合、T 1の長さがT 2の長さより小さく、かつ題意を満たす場合、T 1は必ずT 2の接頭辞であり、T 2がT 1より多く出た列をCとし、複雑度をO(2^n)とする.同様に,後半を列挙する場合,T 1がT 2より多く出た列をCCとする.C=CCのみの場合、T 1=T 2となるので、全てのCに対応するsumからCCに対応するsumの絶対値を減算した最小値が解答となる
最初はソートされていませんが、dfs_がわかります.backは1枚につき1つのCCを挙げて保存したCの中で片側を遍歴し,TLEを招き,さらに問題解をよく見ると,並べ替えた後に隣接する2つのCとCCを列挙すれば,1つ隔てた場合に隣接するものより小さくないことが証明される.
修正後もWAを続け、最後にユニットデータをランダムにしたところ、短い文字列に文字を追加した場合、接頭辞にnum[i]==((c>(j-k-1)&1)を使うべきかどうかを判断し、num[i]=((c&(1<(j-k-1))を書き始めた.あ、この間違いは逆順dfs_backのCC文字列の時に出たことがありますが、タイムリーに修正しました、ここでは注意していません…あとは左右に移動した結果に注意して、当然とは思えません
便宜上ab列をバイナリで表し,また01や001などは区別できないため,列の一番前に1を加えるとすべての長さの異なる列を区別できる.
速度は速くて、2.5 sぐらいで、時間は一時的に2番目に並んでいます:)しかし、空間の占有量はわりに大きいです
#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
");
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
",ans==0x3f3f3f3f?-1:ans);
}
return 0;
}