Codeforces Round #247 (Div. 2)

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值