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度
循環節數 74 20 38 20 26 26
情況種數 1 3 9 3 4 4

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


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章