HDU-4963 Dividing a String (枚举[中途相遇法])

7 篇文章 0 订阅

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
  
  
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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值