2013年多校联合第四场(2013 Multi-University Training Contest 4)解题报告

1001(HDU4632):Palindrome subsequence

这道题是找到一个字符串中不同的回文数量,用区间DP来做,dp[i][j]表示区间[ i , j ]的回文数量,然后递归。

思路:按照长度i从1—n循环递归求出结果:利用变量j从字符串的头开始扫描。

如果s[ j ] != s[ j + i ],dp[ j ][ j + i ] = dp[ j + 1 ][ j + i ] + dp[ j ][ j + i - 1 ] - dp[ j + 1 ][ j + i - 1 ](减去重复的部分);

如果s[ j ] = s[ j + i ],dp[ j ][ j + i] = dp[ j + 1][ j + i ] + dp[ j ][ j + i - 1 ] + 1 (由于s[ j ……j + i - 1]有多少个回文串,那么s[ j + 1……s[ j + i ]就有多少个回文串,所以不用减去中间的交叉部分)。

code:

#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <queue>
#include <stack>
#include <iomanip>
#include <map>
#include <climits>
#include <set>
#include <vector>
#define N 10007
using namespace std;
string s ;
int dp[1111][1111] ;
int main()
{
    int t , i , j ,count = 0 ;
    scanf("%d" , &t) ;
    while(t --)
    {
        memset(dp , 0 , sizeof(dp)) ;

        cin >> s ;
        int l = s.length() ;
        for(i = 0 ; i < l ; i ++)
        {
            dp[i][i] = 1 ;
        }
        for(i = 1 ; i < l ; i++)
        {
            for(j = 0 ; j < l - i ; j ++)
            {
                if(s[j] != s[j + i])
                dp[j][j + i] =(dp[j + 1][j + i] + dp[j][j + i - 1] - dp[j + 1][i + j - 1] + N) % N ;
                else
                dp[j][j + i] =(dp[j + 1][j + i] + dp[j][j + i - 1] + 1 + N) % N ;
            }
        }
        printf("Case %d: %d\n" , ++ count , (dp[0][l-1] + N) % N) ;
    }
    return 0;
}


1002(HDU4633):Who's Aunt Zhang

这道题是组合数学的问题,用到了置换群,还有循环节数。还有一个定理:Polya定理。

Polya定理:设G 是n个对象的一个置换群,用m种颜色涂染这n个对象,则不同染色的方案数为:

其中G={g(1),……,g(n)},c(g(i))是循环节数。

那么本题是魔方置换群,由于是一个立体图形,那么正方体的面9 *6 =54个、顶点8个、棱12个都是属于不同的状态。所以共有74种。但是魔方可以旋转,所以分以下几种情况:

1.不动:循环节数有74个,有1种情况;

2.旋转90度(整体旋转,以下都是):分前后旋转、左右旋转、顺逆旋转3种情况:

循环节数:旋转的两个侧面3(循环节数)*2(情况)=6个,被旋转的4个大面9个,顶点1(循环节数)*2=2个,棱1(循环节数)*3=3个,一共是6+9+2+3=20个;

3.旋转180度:普通旋转:前后、左右、顺逆3种,一组对棱不动上下两地面互换:12条棱6组,有6种,所以3+6=9种:

循环节数:旋转的两个侧面5(循环节数)*2=10个,被旋转的4个面是对面之间互换有9(循环节数)*2=18个。棱2(循环节数)*3=6个,顶点2(循环节数)*2=4种,一共有10+18+6+4=38种;

4.旋转270度:与旋转90度效果一样,循环节数20个,情况3种;

5.旋转120度(绕体对角线旋转):8个顶点,4组体对角线,所以有4种情况:

面9(循环节数)*2=18种,棱(与体对角线链各个端点相连的3*2条棱)1(循环节数)*2=2个,剩下两条棱2(循环节数)*1=2个,顶点(体对角线的两个端点)2(循环节数)*1=2个,剩下的6个顶点2(循环节数)*1=2个,一共有18+2+2+2+2=26种

6.旋转240度:与旋转120度一样。

综上所述:见下表:

 不动旋转90度旋转180度旋转270度旋转120度旋转240度
循环节数742038202626
情况种数139344

所以G=1+3+9+3+4+4=24种情况。

将上述情况代入公式即得出答案。

PS:(1)求幂数的时候要用到快速幂,否则TLE;

         (2)进行模运算的时候要对10007*24进行取模,否则对10007取模不一定被24整除。

code:

#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <queue>
#include <stack>
#include <iomanip>
#include <map>
#include <climits>
#include <set>
#include <vector>
#define N (10007 * 24)//注意:不能把()丢掉
using namespace std;
long long mode(long long a , long long b)
{//快速幂求a^b
    long long ans = 1 ;
    long long t = a ;
    while(b)
    {
        if(b & 1)
        {
            ans = ans * t % N ;
        }
        b >>= 1 ;
        t = t * t % N ;
    }
    return ans ;
}
int main()
{
    int t , n ;
    long long count = 0 , ans ;
    scanf("%d" , &t) ;
    while(t --)
    {
        scanf("%d" , &n) ;
        ans = (mode(n , 74) + mode(n , 20) * 3 * 2 + mode(n , 38) * 9 + mode(n , 26) * 4 * 2) % N ;
        ans = ans / 24 ;
        printf("Case %I64d: %I64d\n" ,++ count , ans) ;
    }
    return 0;
}


1008(HDU4639):Hehe

这道题是找规律的题,一开始我找规律找错了,后来队友说和斐波那契数有关,所以规律就是只要找到”hehe“出现的次数就可以了。

code:

#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <queue>
#include <stack>
#include <iomanip>
#include <map>
#include <climits>
#include <set>
#include <vector>
#define N 10007
using namespace std;
char a[11111] ;
int fab[5555] ;
void init()
{
    int i ;
    fab[1] = 1 ;
    fab[2] = 2 ;
    for(i = 3 ; i <= 5555 ; i ++)
    {
        fab[i] = (fab[i - 1] + fab[i - 2]) % N ;
    }

}
int main()
{
    int t , i , j , sum ;
    cin >> t ;
    init() ;
    int k = 0 ;
    while(t --)
    {
        sum = 1 ;
        int ans = 1 ;
        scanf("%s" , a) ;
        int l = strlen(a) ;
        for(i = 0 ; i <= l - 4 ; i ++)
        {
            if(a[i] == 'h' && a[i + 1] == 'e')
            {
                j = i + 2 ;
                sum = 1 ;
                while(1)
                {
                    if(a[j] == 'h' && a[j + 1] == 'e')
                    {
                        sum ++ ;
                        j += 2 ;
                    }
                    else
                    break ;
                }
                if(sum >= 2)
                {
                    ans = (fab[sum] * ans) % N ;
                }
                i = j - 1 ;
            }
        }
        printf("Case %d: %d\n" , ++ k , ans) ;
    }
    return 0;
}


1011(HDU4642):Fliping game

这是一道很水的博弈,但是当时看到是和矩阵方格有关就觉得是和nim博弈有关,其实是想多了。

我们注意观察:每次进行翻转都会翻转的硬币就是最右下角的那枚硬币,所以Alice最后要么把右下角的从1翻到0,要么就输了。所以只要判断右下角的那枚硬币的朝向就可以了。是1则Alice胜,否则Bob胜。

code:

#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <queue>
#include <stack>
#include <iomanip>
#include <map>
#include <climits>
#include <set>
#include <vector>
using namespace std;
int a[111][111] ;
int main()
{
    int t , n , m ;
    scanf("%d" , &t) ;//不能用cin,会超时
    while(t --)
    {
        scanf("%d%d" , &n , &m) ;
        for(int i = 0 ; i < n ; i ++)
        for(int j = 0 ; j < m ; j ++)
        scanf("%d" , &a[i][j]) ;
        if(a[n - 1][m - 1] == 1)
        printf("Alice\n") ;
        else
        printf("Bob\n") ;
    }
    return 0;
}


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值