BZOJ 1600 [Usaco2008 Oct]建造栅栏:dp【前缀和优化】

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1600

题意:

  给你一个长度为n的木板,让你把这个木板切割成四段(长度为整数),并且要求这四段可以构成一个四边形。

  问你有多少种切割方法(切割点不同就算不同)。

 

题解:

  构成四边形的条件:

    任意一边长度 < 周长/2

    证明:设四边为a,b,c,d。因为有a < b+c+d,所以2*a < a+b+c+d = C,所以a < C/2。

 

  简化问题:

    给你n个小木块,排成一排。问你将这些小木块分成四部分,且能构成四边形的方案数。

 

  表示状态:

    dp[i][j] = combinations

    i:已经选了前i个木块

    j:已经分成了j部分

 

  找出答案:

    ans = dp[n][4]

    第n块已经选了,共被分成了4部分。

 

  如何转移:

    dp[i][j] = ∑ dp[i-k][j-1] (k <= i, k < (n+1)/2)

    同时保证下标 >= 0,以及边长k < 周长/2。

 

  边界条件:

    dp[0][0] = 1

    others = 0

    什么都没选为一种方案。

 

  优化:

    前缀和。

    (其实不优化也能过。。。)

 

AC Code:

 1 // state expression:
 2 // dp[i][j] = combinations
 3 // i: considering ith board
 4 //
 5 // find the answer:
 6 // ans = dp[n][4]
 7 //
 8 // transferring:
 9 // dp[i][j] = sigma dp[i-k][j-1]
10 // k: 1 to min(half,i)
11 //
12 // boundary:
13 // dp[0][0] = 1
14 // others = 0
15 #include <iostream>
16 #include <stdio.h>
17 #include <string.h>
18 #define MAX_N 2505
19 
20 using namespace std;
21 
22 int n;
23 int dp[MAX_N][5];
24 int sum[MAX_N][5];
25 
26 void read()
27 {
28     cin>>n;
29 }
30 
31 int cal_sum(int x,int y,int k)
32 {
33     if(x==0) return sum[y][k];
34     return sum[y][k]-sum[x-1][k];
35 }
36 
37 void update_sum(int x,int y)
38 {
39     if(x==0) sum[x][y]=dp[x][y];
40     else sum[x][y]=sum[x-1][y]+dp[x][y];
41 }
42 
43 void solve()
44 {
45     memset(dp,0,sizeof(dp));
46     dp[0][0]=1;
47     for(int i=0;i<=n;i++)
48     {
49         sum[i][0]=1;
50     }
51     for(int j=1;j<=4;j++)
52     {
53         for(int i=1;i<=n;i++)
54         {
55             dp[i][j]=cal_sum(max(0,i-(n+1)/2+1),i-1,j-1);
56             update_sum(i,j);
57         }
58     }
59 }
60 
61 void print()
62 {
63     cout<<dp[n][4]<<endl;
64 }
65 
66 int main()
67 {
68     read();
69     solve();
70     print();
71 }

 

转载于:https://www.cnblogs.com/Leohh/p/7623597.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值