20150310总结

来源:互联网 发布:交朋友的软件 编辑:程序博客网 时间:2024/06/08 06:03

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]);




0 0