A. Choosing Teams
http://codeforces.com/contest/432/problem/A
题目意思: 现在有n个人 , 告诉你每个人已经参加过几次 world final , 而每个人最多只能参加5次 , 现在我们需要组几支队伍,让他们可以至少参加k次world final , 问最多可以组多少支队伍.
思路: 大水题 ... 只要数数有几个人还能打k年就可以了
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int main(){
int n , k ;
while( scanf( "%d%d" , &n , &k ) != EOF ) {
int peo = 0 ;
for( int i = 0 ; i < n ; i ++ ) {
int a ; scanf( "%d" , &a ) ;
if( 5 - a >= k ) peo ++ ;
}
printf( "%d\n" , peo / 3 ) ;
}
return 0 ;
}
B. Football Kit
http://codeforces.com/contest/432/problem/B
题目意思 : 有n支队伍,每支队伍有两种球衣 , 一种是主场球衣,颜色为x , 一种是客场球衣 , 颜色为y ( x!=y ) 。 每支队伍都需要和其他队伍进行两场比赛,主客各一场,也就是说每支队伍总共要打 2 * ( n - 1 ) 场 。一般来说都是主场穿主场球衣,客场穿客场球衣 , 但问题是有些球队的客场球衣和对方的主场球衣会相撞,那么这种情况下 ,客场球队穿主场球衣。你要求的是所有球队打完比赛之后,穿过几次主场球衣,几次客场球衣。
思路 : 因为总场数是不变的,所以我们只要考虑主场的球衣穿多少遍就可以了。首先,n-1场主场必然是穿主场球衣的 , 那n-1场客场就不一定了,这取决于这只球队的客场球衣和多少球队主场球衣冲突了。因为 球衣颜色的范围不大 , 所以我们只要用一个数组统计下主场球衣每种颜色的个数就可以了。
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define MAXN 100005
int cnt[MAXN] ;
int home[MAXN] , away[MAXN] ;
int main(){
int n ;
while( scanf( "%d" ,&n ) != EOF ) {
memset( cnt , 0 , sizeof(cnt) ) ;
for( int i = 1 ; i <= n ; i ++ ) {
scanf( "%d%d" , &home[i] , &away[i] ) ;
cnt[home[i]] ++ ;
}
for( int i = 1 ; i <= n ; i ++ ) {
int a = n - 1 + cnt[away[i]] ;
printf( "%d %d\n" , a , 2 * ( n - 1 ) - a ) ;
}
}
return 0 ;
}
C. Prime Swaps
http://codeforces.com/contest/432/problem/C
题目意思 : 给你一个 1 ... n 的排列 , 你需要把它变成升序 , 但是只能通过下面操作 :
交换 a[i] , a[j] , 并且 j - i + 1 是一个素数( j > i )
然后让你构造一个可行的方案( 不是最小的方案 ) , 不过这个方案的操作数不能超过 5*n
思路 : 看到题目完全没有其他想法 ... 所以只能坚信那句话 ... 暴力出奇迹 !!
直接从1开始,一个一个的把对应的数移到相应的位置上 。 至于如何找 , 因为相邻的两个素数还是比较靠近的 , 于是就每次去找小于 pos[i] - i + 1 的最大素数( pos[i] 是当前i的下标 ) , 感觉应该这个差会很快就减小( 至于为毛可以小于 5*n ... 我也不知道 , 纯粹的YY ) ...
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
#include <math.h>
using namespace std ;
#define MAXN 100005
int prime[MAXN+5] ;
bool isprime[MAXN+2005] ;
int cntP ;
void getPrime(){
isprime[2] = true ;
for( int i = 3 ; i <= MAXN + 2000 ; i ++ ) {
isprime[i] = i % 2 ;
}
int k = sqrt( 1.0 * (MAXN + 2000 )) ;
for( int i = 3 ; i <= k ; i += 2 ) {
if( isprime[i] ) {
for( int j = i * i , s = 2 * i ; j <= MAXN + 2000 ; j += s ) {
isprime[j] = false ;
}
}
}
cntP = 0 ;
for( int i = 2 ; i <= MAXN ; i ++ ) {
if( isprime[i] ) {
prime[cntP++] = i ;
}
}
}
int a[MAXN] ;
int pos[MAXN] ;
int cal( int n ) {
int pos = upper_bound( prime , prime + cntP , n ) - prime ;
return prime[--pos] ;
}
vector<pair<int,int> > ans ;
int main(){
getPrime() ;
int n ;
while( scanf( "%d" , &n ) != EOF ) {
for( int i = 1 ; i <= n ; i ++ ) {
scanf( "%d" , &a[i] ) ;
pos[a[i]] = i ;
}
ans.clear() ;
for( int i = 1 ; i <= n ; i ++ ) {
while( pos[i] != i ) {
int tmp = cal( pos[i] - i + 1 ) ;
int posa = pos[i] , posb = pos[i] - tmp + 1 ;
pos[i] = posb ; pos[a[posb]] = posa ;
tmp = a[posa] ;
a[posa] = a[posb] ;
a[posb] = tmp ;
ans.push_back( pair<int,int>(posb,posa)) ;
}
}
printf( "%d\n" , ans.size() ) ;
for( int i = 0 ; i < ans.size() ; i ++ ) {
printf( "%d %d\n" , ans[i].first , ans[i].second ) ;
}
}
return 0 ;
}
D. Prefixes and Suffixes
http://codeforces.com/contest/432/problem/D
题目意思 : 给你一个字符串 T , 你需要找到所有这样的子串S , 满足 S 即是T 的前缀 , 又是T 的后缀 , 并且还要求出每个 S 在T串中出现的次数 。
思路: 为了解决这个问题 , 如果我们会快速的计算一个字符串公共的前后缀 , 然后又能计算所有前缀出现的次数 , 那么这个问题就迎刃而解了。
要解决这两个问题都需要利用KMP的next数组的性质 , next[i] 表示的是母串前i个字符组成的字符串的最长前后缀的长度是多少( 这里的前后缀不包括这个串本身 ) 。
用题目中第一个样例举个例子 :
字符串 A B A C A B Anext值 0 0 1 0 1 2 3
上面的next值下标从1开始
可以看出 next[3] =1 表明了 母串前3个字符组成的前缀 ABA 的最长公共前后缀的长度是1,为子串"A"
next[7] = 3 表明了母串前7个字符组成的前缀ABACABA 的最长公共前缀的长度是3 , 为子串"ABA"
那么回到题目上面,我们如何利用这个性质来求出母串的所有前后缀呢 ?
很显然根据next[7] = 3 我们可以知道这个字符串最长的前后缀是3,也就是子串"ABA" , 那么那些短一些的呢 ?
首先思考下这些短一些的前后缀的字符是来自于哪里的? 前缀必然来自前next[7]的字符,而后缀必然来自后next[7]个字符,那后next[7]个字符根据next 数组的定义,又和前next[7]个字符是一样的,那么我们可以得出这样的结论 : 母串那些短的前后缀就是前next[7]个字符组成的子串的前后缀。
接下来就只要根据这个结论“递归”的求解下去就能得到母串所有前后缀的长度了。
接下来的问题就是求解母串中所有前缀出现的次数了 ,这个问题的话,其实以前做到过 hdu 3336 利用KMP+DP求解。
dp[i] 表示长度为i的前缀在母串中出现了多少遍 , 我们计算的时候从后面往前计算
首先根据next数组的定义,如果前缀i出现了,那么前缀next[i]也必然会出现 , 所以我们就根据这个递推
假设我们现在要计算前缀i的dp值( 此时 dp[j] | j > i 已经全部计算完毕了 ) , 那么 dp[i] = Σ( dp[j] | next[j] == i ) + 1
那么问题也就迎刃而解了
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
using namespace std;
char str[100005] ;
int f[100005];
int dp[100005] ;
void getnext ( char *t ) {
int len = strlen ( t );
int i = 0, j = -1;
f[0] = -1;
while ( i < len ) {
if ( j == -1 || t[i] == t[j] ) {
i++;
j++;
f[i] = j;
}
else
j = f[j];
}
}
int main(){
while ( scanf( "%s" , str ) != EOF ) {
getnext( str ) ;
vector<pair<int,int> > ans ;
memset( dp , 0 , sizeof(dp) ) ;
int len = strlen( str ) ;
for( int i = len ; i >= 1 ; i -- ) {
dp[i] ++ ;
dp[f[i]] += dp[i] ;
}
ans.push_back( pair<int,int>( len , 1 ) ) ;
while( f[len] != 0 ) {
len = f[len] ;
ans.push_back( pair<int,int>( len , dp[len] ) ) ;
}
printf( "%d\n" , ans.size() ) ;
sort( ans.begin() , ans.end() ) ;
for( int i = 0 ; i < ans.size() ; i ++ )
printf( "%d %d\n" , ans[i].first , ans[i].second ) ;
}
return 0 ;
}
E. Square Tiling
http://codeforces.com/contest/432/problem/E
题目意思 : 给你一个n*m的矩形,你需要给这个矩形涂颜色,但是有以下要求 :
1. 你只能涂正方形的颜色块
2. 相邻的颜色块不能同色
颜色用大写字母表示 , 你需要求出一个符合上述要求的字典序最小的方案( 字典序是从左到右从上到下来比较的)
思路 : 一开始天真的以为只要每次找到左上角,然后尽量涂比较大的正方形颜色块就行了 , 然后就WA case6 了 ...
后来想想不能是尽量放得大 ,而是尽量放得小,这样才可以留下空间给后面的格子放字典序小的字母,所以我后来的做法是,给第一个未涂色的格子涂上颜色,然后根据周围的情况判断能不能涂这种颜色,能就涂上,这样就可以给后面的格子留下空间,让他们涂字典序比较小的
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int n , m ;
char str[105][105] ;
bool check( int x , int y , char ch ) {
if( str[x][y+1] == ch ) {
return false ;
}
if( str[x-1][y] != ch && str[x][y-1] != ch ) {
str[x][y] = ch ;
return true ;
}
if( str[x-1][y] == ch ) {
int len = 1 ;
int xx = x - 1 , yy = y ;
while( str[xx][y] == ch ) {
len ++ ;
xx -- ;
}
xx ++ ;
while( str[xx][yy] == ch ) {
yy -- ;
}
yy ++ ;
if( xx + len - 1 > n || yy + len - 1 > m ) return false ;
for( int i = 1 ; i <= len ; i ++ ) {
for( int j = 1 ; j <= len ; j ++ ) {
if( str[xx + i - 1 ][ yy + j - 1 ] != ch && str[xx + i - 1 ][yy + j - 1 ] != 0 ) return false ;
}
}
for( int i = 1 ; i <= len ; i ++ ) {
for( int j = 1 ; j <= len ;j ++ ) {
str[ xx + i - 1 ][ yy + j - 1 ] = ch ;
}
}
return true ;
}else{
int len = 1 ;
int xx = x , yy = y - 1 ;
while( str[xx][yy] == ch ) {
len ++ ;
yy -- ;
}
yy ++ ;
while( str[xx][yy] == ch ) {
xx -- ;
}
xx ++ ;
if( xx + len - 1 > n || yy + len - 1 > m ) return false ;
for( int i = 1 ; i <= len ; i ++ ) {
for( int j = 1 ; j <= len ; j ++ ) {
if( str[xx + i - 1 ][ yy + j - 1 ] != ch && str[xx + i - 1 ][yy + j - 1 ] != 0 ) return false ;
}
}
for( int i = 1 ; i <= len ; i ++ ) {
for( int j = 1 ; j <= len ;j ++ ) {
str[ xx + i - 1 ][ yy + j - 1 ] = ch ;
}
}
return true ;
}
}
int main(){
while( scanf( "%d%d" , &n , &m ) != EOF ) {
memset( str , 0 , sizeof(str) ) ;
for( int i = 1 ; i <= n ; i ++ ) {
for( int j = 1 ; j <= m ; j ++ ) {
if( str[i][j] == 0 ) {
for( char ch = 'A' ; ch <= 'Z' ; ch ++ ) {
if( check( i , j , ch ) ) {
break;
}
}
}
}
}
for( int i = 1 ; i <= n; i ++ ) {
for( int j = 1 ; j <= m ; j ++ )
putchar( str[i][j] ) ;
puts( "" ) ;
}
}
return 0 ;
}