A. Black Square
题目意思 : 给你4个数a1,a2,a3,a4。给你一个字符串由1,2,3,4,组成,消掉1花费a1,消掉2花费a2,消掉3花费a3,消掉4花费a4
思路 : 大水题...直接模拟
#include <stdio.h>
#include <string.h>
char str[100005] ;
int main(){
int a[8] ;
scanf( "%d%d%d%d" , &a[1] , &a[2] ,&a[3] , &a[4] ) ;
scanf( "%s" , str ) ;
int len = strlen( str ) ;
int sum = 0 ;
for( int i = 0 ; i < len ; i ++ ) {
sum += a[str[i]-'0'] ;
}
printf( "%d\n" , sum ) ;
return 0 ;
}
B. Shower Line
题目意思 : 给你一个5*5的矩阵,以及一个5个同学,下标为1,2,3,4,5,你需要给这5个同学排个顺序,让他们得到的快乐值最大。
快乐值如何计算,第一个人和第二个人交谈,第三个人和第四个人交谈, 然后在第二个人和第三个人交谈,第四个人和第五个人交谈,然后第三个人和第四个人交谈,然后第四个人和第五个人交谈。 每次两个人交谈都会获得快乐值,下标为i,j的两个人交谈,获得的快乐值为g[i][j]+g[j][i]
思路 :直接枚举所有的顺序,再按照题意计算即可。 枚举顺序的话,可以直接5个for,或者 next_permutation 都行
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int g[8][8] ;
int main(){
int Max = 0 ;
for( int i = 1 ; i <= 5 ; i ++ ) {
for( int j = 1 ; j <= 5 ; j ++ ) {
scanf( "%d" , &g[i][j] ) ;
}
}
int pos[8] = { 0 , 1 , 2 , 3 , 4 , 5 } ;
do{
int ans = 0 ;
for( int i = 0 ; i < 5 ;i ++ ) {
for( int j = i + 1 ; j + 1 <= 5 ; j += 2 ) {
ans += g[pos[j]][pos[j+1]] + g[pos[j+1]][pos[j]] ;
}
}
Max = max( Max , ans ) ;
}while( next_permutation( pos + 1 , pos + 1 + 5 ) ) ;
printf( "%d\n" , Max ) ;
return 0 ;
}
C. k-Tree
题目意思 : 其实就是利用 1~k 并且至少有一个数字大于等于d ,组成n的方案数有多少 ( 1 1 2 , 1 2 1 算两个不同的方案 )
思路 : 因为n和k就只有100,所有直接dp就可以了,我的做法是先算出用1~k组成n的所有方案数,再计算出1~d-1的所有方案数,两个相减即可
计算用1~k组成n的所有方案数 : dp[i][j] 表示用i个数字组成j的方案数是多少, dp[i][j] = Σ( dp[i-1][j-kk] | 1<=kk <= k && j - kk >= 0 )
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define mod 1000000007
__int64 dp[105][105] ;
int n , k , d ;
int main(){
while( scanf( "%d%d%d" , &n , &k , &d ) != EOF ) {
memset( dp , 0 , sizeof(dp) ) ;
dp[0][0] = 1 ;
for( int i = 0 ; i <= n ; i ++ ) {
for( int j = 0 ; j <= n ; j ++ ) {
if( dp[i][j] ) {
for( int kk = 1; kk <= k && j + kk <= n; kk ++ ) {
dp[i+1][j+kk] += dp[i][j] ;
dp[i+1][j+kk] %= mod ;
}
}
}
}
__int64 ans1 = 0 ;
for( int i = 1 ; i <= n ; i ++ ) {
ans1 += dp[i][n] ;
ans1 %= mod ;
}
memset( dp , 0 , sizeof(dp) ) ;
dp[0][0] = 1 ;
for( int i = 0 ; i <= n ; i ++ ) {
for( int j = 0 ; j <= n ; j ++ ) {
if( dp[i][j] ) {
for( int kk = 1; kk <= d - 1 && j + kk <= n ; kk ++ ) {
dp[i+1][j+kk] += dp[i][j] ;
dp[i+1][j+kk] %= mod ;
}
}
}
}
__int64 ans2 = 0 ;
for( int i = 1 ; i <= n ; i ++ ) {
ans2 += dp[i][n] ;
ans2 %= mod ;
}
printf( "%I64d\n" , (ans1 - ans2 + mod ) % mod ) ;
}
return 0 ;
}
D. Random Task
题目意思 : 找到一个n,使得 n+1,n+2...2*n 中有m个数的二进制中1的个数为k
思路 : 这题真是淡淡的忧伤 ... 二分因为少写一个等号fst了 T。T .. 本来这场打得挺满意的 ... 结果 ... 哎 ... 果然会写二分的程序员很值钱,此言不假
看到10^18的数据量,就感觉到这是数位DP。但是即使知道是数位dp还是挺难求的,于是YY...如果对于同一个K,n越大,满足题意的范围内二进制中1的个数为k的数量是不递减的,就可以直接二分搞了。 于是打表验证这个YY,发现确实如此 ... 那就直接二分吧 ...
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
// 二进制前i个数字包含k个数字的大小是多少
__int64 dp[68][68] ;
int digit[68] ;
__int64 m ;
int k;
__int64 dfs( int pos , int one , bool limit ) {
if( one < 0 ) return 0 ;
if( pos == -1 ) return one == 0 ;
if( !limit && dp[pos][one] != -1 )
return dp[pos][one] ;
int size = limit?digit[pos]:1 ;
__int64 sum = 0 ;
for( int i = 0 ; i <= size ; i ++ ) {
sum += dfs( pos -1 , one - i , limit&&i==size);
}
if( !limit )dp[pos][one] = sum;
return sum ;
}
__int64 c( __int64 n , int k ) {
int len = 0 ;
while( n ){
digit[len++]=n%2;
n/=2;
}
return dfs( len-1 , k , true );
}
__int64 cal( __int64 n , int k ) {
return c( 2 * n , k ) - c( n , k ) ;
}
int main(){
memset(dp,-1,sizeof(dp));
while( scanf( "%I64d%d" , &m ,&k ) != EOF ) {
__int64 l = 1 , r = 1000000000000000000 ;
__int64 mid ;
__int64 ans ;
while( l <= r ) {
mid = ( l + r ) >> 1 ;
__int64 tmp ;
tmp = cal( mid , k ) ;
if( tmp == m ) {
ans = mid ;
break;
}else if( tmp > m ) {
r = mid - 1 ;
}else{
l = mid + 1 ;
}
}
printf( "%I64d\n" , ans ) ;
}
return 0 ;
}