ECNA 2014_湖南多校赛2015.05.17 (待补)

B.A Cure for the Common Code

题目描述:
    给定一个长度为500的字符串,连续重复子串可缩写为X(S),X为重复次数,S为最小子串,若S长度为1不用写括号
    例:abcbcbcbca -> a4(bc)a,问最短的缩写长度是多少
思路:
    DP.
    F[I][J]表示区间[I,J]的最短长度,初始F[I][J] = J - I + 1,共两种转移:
1.min{F[I][K]+[K+1][J]}, I<=K<J 俩子区间
2.min{len(S)+F[I][I+S-1]+(S == 1 ? 0 : 2)}  缩写
    区间DP时间复杂度O(N^3),枚举重复子区间一个平行的O(N^2*Sigma(i的约数和)) 近似 O(N^3logN)复杂度,实际上达不到这个上界

 
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
#define foru(i, a, b) for(int i=(a); i<=(b); i++)
#define ford(i, a, b) for(int i=(a); i>=(b); i--)

const int N = 510;

int n;
int f[N][N];
char S[N];

int len(int x){
    if (x >= 100) return 3;
    if (x >= 10) return 2;
    return 1;
}

int main(){
    int cas = 0;
    while(scanf("%s", S) != EOF){
        if (S[0] == '0') break;
        n = strlen(S) - 1;

        foru(i, 0, n)
            foru(j, i, n)
                f[i][j] = j - i + 1;

        foru(l, 3, n+1){
            foru(i, 0, n){
                int j = i + l - 1;
                if (j > n) break;

                foru(k, i, j-1)
                    f[i][j] = min(f[i][j], f[i][k] + f[k+1][j]);

                foru(c, 1, l-1){
                    if (l % c > 0) continue;
                    bool OK = 1;
                    foru(k, 0, l-c-1){
                        if (S[i+k] != S[i+k+c]){
                            OK = 0;
                            break;
                        }
                    }
                    if (OK && c == 1) f[i][j] = min(f[i][j], len(l) + f[i][i+c-1]);
                    if (OK && c >  1) f[i][j] = min(f[i][j], len(l/c) + 2 + f[i][i+c-1]);
                }
            }
        }
        printf("Case %d: %d\n", ++cas, f[0][n]);
    }
    return 0;
}

C.Domiyahtzee!

题目描述:
    给定一个5*5网格,有21个长度为2的骨牌(1,1),(1,2)..(2,2),(2,3)..(6,6)
    用12个骨牌和一个1*1单牌不重叠的填满网格,可以用剩下的9个骨牌与网格中的任意一个交换
    问5*5网格中的每行,每列,每个对角线总共12个排列的得分之和最大是多少
    得分计算如下:
         三个相同的X 3 * X
         四个相同的X 4 * X
         三个相同的和一个对子 25
         长度为4的顺子 30
         长度为5的顺子 40
         5个相同的X 第一次出现 50 然后每次出现 100
思路:
    大模拟.
    暴力放入12张骨牌,记录每个骨牌的位置和方向,找出剩余的9张骨牌
    枚举位置和剩余的骨牌,枚举骨牌前把9个骨牌的另一种方向存起来,变成18张骨牌
    计算得分:用S[i]表示面值i的出现次数,X[i]表示出现次数为i的面值,这样可以很容易算出得分


#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
#define foru(i, a, b) for(int i=(a);i<=(b);i++)
#define ford(i, a, b) for(int i=(a);i>=(b);i--)

int m;
int px, py;
int Fig[6][6];
int PosX[15], PosY[15];
int lx[20], ly[20];

void getAvl(int k){
    foru(i, 1, 5) foru(j, 1, 5)
        if (! Fig[i][j]) {
            px = i; py = j;
            PosX[k] = px;
            PosY[k] = py;
            return;
        }
}

int x1, x2;
char dir[15][3];
bool avl[7][7];
void init(){
    memset(avl, 0, sizeof(avl));
    memset(Fig, 0, sizeof(Fig));
    foru(i, 1, 13){
        getAvl(i);
        scanf("%s", dir[i]);
        if(dir[i][0] == 'S') {
            scanf("%d", &x1);
            Fig[px][py] = x1;
            continue;
        }
        scanf("%d %d", &x1, &x2);
        avl[x1][x2] = avl[x2][x1] = 1;
        if (dir[i][0] == 'H') {
            Fig[px][py] = x1;
            Fig[px][py+1] = x2;
        }
        else if (dir[i][0] == 'V'){
            Fig[px][py] = x1;
            Fig[px+1][py] = x2;
        }
    }
    m = 0;
    foru(i, 1, 6) foru(j, i, 6)
        if (! avl[i][j]) {lx[++m] = i; ly[m] = j;}
    foru(i, 10, 18) lx[i] = ly[i-9], ly[i] = lx[i-9];
}

int D[10], X[10], S[10];
int Yhzee, con, Mcon;
int Yahtzee(){
    Mcon = 1; con = 0;
    memset(X, 0, sizeof(X));
    memset(S, 0, sizeof(S));
    foru(i, 1, 5) S[D[i]] ++;
    foru(i, 1, 6) X[S[i]] = i;
    foru(i, 1, 7)
        if (S[i] > 0) con ++;
        else {Mcon = max(Mcon, con); con = 0;}
    if (X[5] > 0) {
        Yhzee ++;
        if (Yhzee > 1) return 100;
            else return 50;
    }
    if (X[4] > 0) return 4 * X[4];
    if (X[3] > 0 && X[2] > 0) return 25;
    if (X[3] > 0) return 3 * X[3];
    if (Mcon == 5) return 40;
    if (Mcon == 4) return 30;
    return 0;
}

/*void Print(){
    int tmp = Yahtzee();
    foru(i, 1, 5) cout <<D[i] << " ";
    cout << " : " <<Mcon <<endl;
    cout << "SCORE : " <<tmp <<endl;
}*/

int DomiYahtzee(){
    int sum = 0; Yhzee = 0;
    foru(i, 1, 5) D[i] = Fig[i][i]; sum += Yahtzee();
    foru(i, 1, 5) D[i] = Fig[i][5-i+1]; sum += Yahtzee();
    foru(i, 1, 5) {
        foru(j, 1, 5) D[j] = Fig[i][j]; sum += Yahtzee();
        foru(j, 1, 5) D[j] = Fig[j][i]; sum += Yahtzee();
    }
    return sum;
}

int ans;
int tx, ty;
void solve(){
    ans = DomiYahtzee();
    //cout << ans <<endl;
    foru(i, 1, 13){
        if (dir[i][0] == 'S') continue;
        foru(j, 1, 18){
            if (dir[i][0] == 'H'){
                tx = Fig[PosX[i]][PosY[i]];
                ty = Fig[PosX[i]][PosY[i]+1];
                Fig[PosX[i]][PosY[i]] = lx[j];
                Fig[PosX[i]][PosY[i]+1] = ly[j];
                ans = max(ans, DomiYahtzee());
                Fig[PosX[i]][PosY[i]] = tx;
                Fig[PosX[i]][PosY[i]+1] = ty;
            }
            else{
                tx = Fig[PosX[i]][PosY[i]];
                ty = Fig[PosX[i]+1][PosY[i]];
                Fig[PosX[i]][PosY[i]] = lx[j];
                Fig[PosX[i]+1][PosY[i]] = ly[j];
                ans = max(ans, DomiYahtzee());
                Fig[PosX[i]][PosY[i]] = tx;
                Fig[PosX[i]+1][PosY[i]] = ty;
            }
        }
    }
}

int main(){
    freopen("C.in", "r", stdin);
    int T, cas = 0; scanf("%d", &T);
    while(T--){
        init();
        solve();
        printf("Case %d: %d\n", ++cas, ans);
    }

    return 0;
}


D.Generalized Roman Numerals

题目描述:
    5种罗马数字I,V,X,C,L分别表示1,5,10,50,100
    给定一串长度为50的罗马数字,可以任意添加括号,计算方式如下:
       如果前一个数字比后一个小,用后一个减去前一个 IV = 4 IX = 9
       否则表示俩个罗马数字之和
    询问能得到多少种大小的数字
思路:
    DP.
    考虑n = 50, 那么数字最大为 50 * 100 = 5000
    用F[I][J][K]表示区间F[I][J]中能得到的数字种类,其中K是一个5000的状态
    区间[I,J]的枚举按照一般区间DP,时间复杂度为50^3,发现K的枚举需要优化
    把第三维改成一个VECTOR,转移的时候初始一个5000的桶,对转移的俩个区间合并,合并完把桶中的数存入新的状态,这样就得到近似50^3 * 5000的算法

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
#define foru(i, a, b) for(int i=(a); i<=(b); i++)
#define ford(i, a, b) for(int i=(a); i>=(b); i--)

const int M = 5010;
const int N = 60;

int V(char c){
    if (c == 'I') return 1;
    if (c == 'V') return 5;
    if (c == 'X') return 10;
    if (c == 'L') return 50;
    if (c == 'C') return 100;
}

int cas;
int n, m;
int f[N][N][M], g[N][N];
bool use[M];
char S[N];
int a[N];

int x, y;
void Merge(int l1, int r1, int l2, int r2){
    foru(i, 1, g[l1][r1]){
        x = f[l1][r1][i];
        foru(j, 1, g[l2][r2]){
            y = f[l2][r2][j];
            if (x < y) use[y - x] = 1;
            else use[x + y] = 1;
        }
    }
}

int main(){
    while (scanf("%s", S) != EOF){
        if (S[0] == '0') break;
        n = strlen(S); m = 0;
        foru(i, 1, n) a[i] = V(S[i-1]), m += a[i];

        foru(i, 1, n) g[i][i] = 1, f[i][i][1] = a[i];
        foru(l, 2, n){
            foru(i, 1, n){
                int j = i + l - 1;
                if (j > n) break;
                memset(use, 0, sizeof(use));
                foru(k, i, j - 1)
                    Merge(i, k, k+1, j);
                g[i][j] = 0;
                foru(k, 0, m)
                    if (use[k]) f[i][j][++g[i][j]] = k;
            }
        }


        printf("Case %d:", ++cas);
        foru(i, 1, g[1][n]) printf(" %d", f[1][n][i]);
        printf("\n");
    }
    return 0 ;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值