Codeforces Round #160 (Div. 2)(完全)

本文总结了作者在CodeForces平台的一次比赛中遇到的问题及解决思路,包括使用暴力算法解决A题,贪心策略解决C题,以及如何通过背包DP方法解决D题的过程。同时分享了在E题中利用数位DP技巧解决问题的经验。

考试结束后的第一场CF, 诶,功力不够,B,C都出了点小问题,都绿色了还跌,变紫任重而道远。

http://www.codeforces.com/contest/262

A. 暴力

B.  注意要把k次用完,我图快没看清楚,WA了一次。

C. 贪心,q[]数组排序一下,只要取最小的那个q[]去打折,不能打折的分开买,很容易证明这种方案是最优的, 我很早就想到了,当时总觉得这种方案不一定最优,一直再举反例,太失败了, 后来才发现这是最优的,但赛后由于一个小细节在改得时候没改过来RE掉了,诶,无奈跌分。

D . 背包DP, 比赛的时候想到了背包,但本人水平太弱,思路无法构建,赛后看别人代码,很久才吃透为什么要那么做,好题啊,思维又有了提高。

思路:

先处理出dp[i][j](表示排了i个人,占桌子的长度为j的情况有几种)

// 方法一: 
dp[0][0] = 1;
    double ans = 0;
    for(i = 0; i < n; i++)
        for(k = p; k >= a[i]; k--)
            for(j = 1; j <= i+1; j++)
                dp[j][k] += dp[j-1][k-a[i]];

//方法二:
dp[0][0] = 1;

    for(i = 0; i < n; i++)
        for(j = n; j > 0; j--)
            for(k = a[i]; k <= p; k++)
                    dp[j][k]+=dp[j-1][k-a[i]];  

以上两种只是状态转移时先后顺序不同。

预处理以后,我们要想把所有情况都列一遍,所以有一下做法

f[i]*f[n-i]*dp[i][j] 表示计算所有第i位的排列总数

   for(i = 1; i <= n; i++)
        for(j = 1; j <= p; j++)
            ans += f[i]*f[n-i]*dp[i][j];
    printf("%.10lf\n", ans/f[n]);


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
double dp[51][51], f[51];
int a[51];

int main()
{
    int i, j, k, n, p;
    f[0] = 1;
    for(i = 1; i <= 50; i++) 
        f[i] = f[i-1] * i;
    scanf("%d", &n);
    for(i = 0; i < n; i++) 
        scanf("%d", &a[i]);
    scanf("%d", &p);
    dp[0][0] = 1;
    double ans = 0;
    for(i = 0; i < n; i++)
        for(j = n; j > 0; j--)
            for(k = a[i]; k <= p; k++)
                    dp[j][k]+=dp[j-1][k-a[i]];  
//  for(i = 1; i <= n; i++)
//      for(j = 1; j <= p; j++)
//          printf("dp[%d][%d] = %.9f\n", i, j, dp[i][j]);
    for(i = 1; i <= n; i++)
        for(j = 1; j <= p; j++)
            ans += f[i]*f[n-i]*dp[i][j];
    printf("%.10lf\n", ans/f[n]);
    return 0;
}


E. 观察题目数据, 第m+1层的和肯定有规律, 打表找规律, 但自己找了好久都没找到, 得到大神的结论 sum=2^(bit_count(m+1)-1) (证明不会)。

然后问题转化为: 找2到m+1 表示成二进制的这些数中 各位之和 为 log2 t + 1 的个数, 显然 当 t 不为 2 的 整数倍时, 答案为0。

这不是数位DP吗?

先预处理dp[i][j](表示二进制数位为i,各位数的总和为j 的数有几种)

dp[1][1] = dp[1][0] = 1;
	for(i = 2; i <= 53; i++)
	{
		for(j = 1; j <= i; j++)
			dp[i][j] = dp[i-1][j-1] + dp[i-1][j];
		dp[i][0] = dp[i-1][0];
	}
然后逐位降位处理:

数位DP两道入门题:light OJ   : http://blog.youkuaiyun.com/c3568/article/details/8504560

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define LL __int64
int a[55];
LL dp[55][55];

void init()
{
	int i, j;
	dp[1][1] = dp[1][0] = 1;
	for(i = 2; i <= 53; i++)
	{
		for(j = 1; j <= i; j++)
			dp[i][j] = dp[i-1][j-1] + dp[i-1][j];
		dp[i][0] = dp[i-1][0];
	}
}
void update(LL n, int &num)
{
	num = 0;
	while(n) {a[num++] = n % 2; n /= 2;}
}

LL gao(LL n, LL t)
{
	int tot = 0;
	while((t&1) == 0) {t >>= 1; tot++;}
	if(t != 1) return 0;
	t = tot+1;
	int num; update(n, num);
	LL ans = 0, cnt = 0;
	int i;
	for(i = num-1; i >= 0; i--)
	{
		if(a[i])
		{
			if(t >= cnt)
				ans += dp[i][t-cnt];
			cnt++;
		}
	}
	if(t == 1) ans--;
	if(cnt == t) ans++;
	return ans;
}
int main()
{
	int i, j;	
	LL n, t;
	init();
	int a;
	scanf("%I64d%I64d", &n, &t);
	printf("%I64d\n", gao(n+1, t));
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值