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