20150310总结

sence:

题目大意:一个目标串k,模式串集合S,现在两人轮流从k的头或尾删去一个字符,在某一次操作后k' ∈ S,则操作者胜出。问先手者是否有必胜策略。

解法:观察,进行递推:

k = abc, sg = 0;

k = xabc || k = abcx sg = 1;

k = xabcx, sg = 0;

k = xabcxx || k = xxabcx; sg = 1;

k = ...xabc.. || k = ..abcx.. [len(...x) > len(..)]; sg = 0;

......

所以当且仅当s匹配k且s在k正中央时(s ∈ S),存在必胜策略(当时没想到这一点,用kmp进行匹配,计算了所有可能的匹配方案,而其中大部分是可以直接判断不合法的,浪费了时间,所以TLE)。

对于一个s,如果 len(k) - len(s) 为偶数,则一定不存在必胜策略;

否则从k[(len(k) - len(s)) / 2]以及k[(len(k) - len(s)) / 2 + 1]开始匹配,如果s匹配k,则存在必胜策略;

否则不存在。

算法复杂度为线性。

inline bool check(void)
{
    int p = klen = slen = 0;
    memset(k, 0, sizeof(k));
    memset(s, 0, sizeof(s));

    scanf("%s%d", k, &p);

    klen = strlen(k);

    for (int i = 1; i <= p; ++i)
    {
        scanf("%s", s);
        slen = strlen(s);

        if (slen > klen) continue;

        if (((!(klen & 1)) && (slen & 1)) || ((klen & 1) && (!(slen & 1))))
        {
            int v = (klen - slen) >> 1;
            bool flag = 1;

            for (int j = 0; j < slen; ++j)
            {
                if (k[v + j] != s[j])
                {
                    flag = 0;
                    break;
                }
            }
            if (flag)
            {
                for (int j = i + 1; j <= p; j ++) scanf("%s", s);
                return 1;
            }

            v ++;
            flag = 1;
            for (int j = 0; j < slen; ++j)
            {
                if (k[v + j] != s[j])
                {
                    flag = 0;
                    break;
                }
            }

            if (flag)
            {
                for (int j = i + 1; j <= p; ++j) scanf("%s", s);
                return 1;
            }
<pre name="code" class="cpp">        }
    }
    return 0;
}

 

tank:

题目大意:已知m个整数,整数x(1≤x≤1023);在这m个整数中取n个数出来,与x按位取与值为0。求n的期望值。

解法:记忆化搜索推导dp方程,类似于01背包,f[i][j][k]当前选到第i个数,已经选了j个数,x的值为k时,当前状态对答案的贡献。

第一维可以滚动数组滚掉;转移方程: f[j + 1][k & v[i]] += f[j][k] / (m - j),注意f[j][k] > 0。

ans = Σ(i = 1, i ≤ n)((Π(1, i) - Π(1, i - 1)) * i)。

    for (int i = 0; i < m; ++ i)
    {
        for (int j = i; j >= 0; -- j)
        {
            for (int k = 0; k < 1024; ++ k)
            {
                if (f[j][k] > 0) f[j + 1][k & v[i]] += f[j][k] / (double)(m - j);
            }
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; ++i)
    {
        double sc(f[i][0]), sp(f[i - 1][0]);
        for (int j = 1; j < i; ++j) sp *= j;
        for (int j = 1; j <= i; ++j) sc *= j;
        ans += (sc - sp) * i;
    }

terroris:

题目大意:给定一个串长为k,一个空串,有4种操作:

1,让当前串变为k的n长前缀,花费n * A;

2.花费B使当前串倍增;

3,花费C删去当前串任意长度后缀;

4,花费D在当前串末尾加上一个字符。

问最小花费

解法:记录当前串hash值,当前操作,当前操作数,然后记忆化搜索或者dp;

dp[i][j] = min(dp[i->pre()][1~4]);




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值