UVa 1009 Sharing Chocolate (数位dp)

题目链接:

  https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3540

题目大意:

  给一块长x,宽y的巧克力,和一个数组A={a1, a2, …,an},问能否经过若干次切分后,得到面积分别为a1,a2,…an的n块巧克力。每次切分只可以选择一块巧克力,将其分为两半,如下图,3×4的巧克力经过切分后,可以得到面积分别为6,3,2,1的巧克力。

解题思路:

  假设能得到n块小巧克力,考虑切分的过程,第一次切分后巧克力被分为两部分,最终结果中的任一快巧克力a[i]要么来自第一部分,要么来自第二部分,即两部分分别对应一个A的子集。那么枚举A的子集A0,另A1=A-A0,如果能找到当前巧克力的一种切分方式,让第一部分能分成A0对应的小巧克力,第二部分分成A1对应的小巧克力,则找到了一组合法的解。

  定义dp状态如下,dp[x][S](S是二进制表示的集合)表示边长分别为x, S对应面积/x的巧克力能否切分成S对应集合,若能则为1,否则为0。考虑到边长x*y=面积,因此只保留一个边长,另一边可以求出来。

  此代码中枚举子集的方法是数位dp的一个技巧。

参考代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 #define N 16
 6
 7 bool f[105][1<<N];
 8 bool vis[105][1<<N];
 9 int A[N], sum[1<<N];
10 int cntbit(int x)
11 {
12     int ret = 0;
13     while(x) ret += x&1, x >>= 1;
14     return ret;
15 }
16
17 bool dp(int x, int cur)//cur用二进制表示当前集合
18 {
19     if(vis[x][cur] == 1) return f[x][cur];
20     vis[x][cur] = 1;
21     bool &ans = f[x][cur];
22     int y = sum[cur]/x;
23     if(cntbit(cur) == 1)
24     {
25         vis[x][cur] = 1;
26         return ans = true;
27     }
28     for(int s0 = (cur-1)&cur; s0; s0 = (s0-1) & cur)//枚举子集的方法
29     {
30         int s1 = cur-s0;
31         if(sum[s0]%x == 0 && dp(min(x, sum[s0]/x), s0) && dp(min(x, sum[s1]/x), s1))
32             return ans = 1;
33         if(sum[s0]%y == 0 && dp(min(y, sum[s0]/y), s0) && dp(min(y, sum[s1]/y), s1))
34             return ans = 1;
35     }
36     return ans = 0;
37 }
38
39 int main()
40 {
41     int n, x, y, cas = 1;
42     while(~scanf("%d", &n), n)
43     {
44         scanf("%d %d", &x, &y);
45         for(int i = 0; i < n; i++) scanf("%d", &A[i]);
46
47         memset(sum, 0, sizeof(sum));
48         for(int i = 0; i < (1<<n); i++)
49             for(int j = 0; j < n; j++) if(i&(1<<j)) sum[i] += A[j];
50
51         int d = (1<<n)-1;
52         if(sum[d] != x*y)
53         {
54             printf("Case %d: No\n", cas++);
55             continue;
56         }
57
58         memset(vis, 0, sizeof(vis));
59         bool ans = dp(min(x, y), d);
60         printf("Case %d: ", cas++);
61         puts(ans ? "Yes" : "No");
62     }
63     return 0;
64 }
时间: 2024-08-28 06:24:49

UVa 1009 Sharing Chocolate (数位dp)的相关文章

uva 417 - Word Index(数位dp)

题目连接:uva 417 - Word Index 题目大意:按照题目中的要求,为字符串编号,现在给出字符串,问说编号为多少,注意字符串必须为递增的,否则编号为0. 解题思路:其实就是算说比给定字符串小并且满足递增的串由多少个.dp[i][j]表示第i个位为j满足比给定字符串小并且满足递增的串. dp[i][j]=∑k=0j?1dp[i?1][k]. 注意每次要处理边界的情况,并且最后要加上自身串.并且在处理边界的时候dp[i][0]要被赋值为1,代表前i个为空的情况. #include <cs

uva 1489 - Math teacher&#39;s homework(数位dp)

题目链接:uva 1489 - Math teacher's homework 题目大意:给定n,k,以及序列m1,m2,-,mn, 要求找到一个长度为n的序列,满足0<=xi<=mi, 并且x1XORx2XOR-XORxn=k 解题思路:数位dp,在网上看了别人的代码,高大上... 假设有二进制数 k : 00001xxxx mi:0001xxxxx, 那么对于xi即可以满足任意的x1XORx2XOR-XORxi?1XORxi+1XOR-XORxn,根据这一点进行数位dp. dp[i][j]

uva 11361 Investigating Div-Sum Property 数位dp

// uva 11361 Investigating Div-Sum Property 数位dp // // 题目大意: // // 给你一个整数a和一个整数b,问在[a,b]范围内,有多少个自身被k整除并且 // 各位数之和也能被k整除.比如k = 7 ,322满足条件,因为332能被整除7,并 // 3 + 2 + 2 = 7 也能被7整除 // // 解题思路: // // 求一个区间的题目,这类题目,往往可以转化为不超过x的f(x).则最后要求 // 的就是f(b) - f(a-1).如

UVA 11361 - Investigating Div-Sum Property(数位DP)

题目链接:11361 - Investigating Div-Sum Property 白书上的例题,不过没有代码,正好前几天写了一题数位DP的题目,这题也就相对轻松了. dp[i][x][y]表示加到第i位,数字 % k,数位和 % k的组合情况数,那么现在要添加一个0 - 9的数字上去状态转移为 dp[i + 1][(x * 10 + num) % k][(y + num) % k],计算总和后,由于数字本身不能超过最大值,所以最后还要添加上恰好前几位都为最大值的情况.然后最后在判断一下该数

uva 10712 - Count the Numbers(数位dp)

题目链接:uva 10712 - Count the Numbers 题目大意:给出n,a,b:问说在a到b之间有多少个n. 解题思路:数位dp,dp[i][j][x][y]表示第i位为j的时候,x是否前面是相等的,y是否已经出现过n.对于n=0的情况要特殊处理前导0,写的非常乱,搓死. #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using na

51nod 1009 数位dp入门

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1009 1009 数字1的数量 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 收藏 关注 给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数. 例如:n = 12,包含了5个1.1,10,12共包含3个1,11包含2个1,总共5个1. Input 输入N(1 <= N <= 10^9) Output 输出

UVA - 11038 How Many O&#39;s? (数位dp)

How Many O's? 题意是求区间内数字中0的个数,比如100就有两个0. 数位dp吧,dp[i][j][k], i很明显表示当前位置,j表示找到的0的个数,k表示要找的0的个数.因为数字里0的个数最多32个,所以可以枚举32种k的情况,用数位dp去找. #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; #

51nod 1009 - 数字1的数量 - [数位DP][模板的应用以及解释]

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1009 基准时间限制:1 秒 空间限制:131072 KB 给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数. 例如:n = 12,包含了5个1.1,10,12共包含3个1,11包含2个1,总共5个1. Input 输入N(1 <= N <= 10^9) Output 输出包含1的个数 Input示例 12 Output示例

51Nod 1009 数字1的个数 | 数位DP

题意: 小于等于n的所有数中1的出现次数 分析: 数位DP 预处理dp[i][j]存 从1~以j开头的i位数中有几个1,那么转移方程为: if(j == 1) dp[i][j] = dp[i-1][9]*2+pow(10,i-1);else dp[i][j] = dp[i-1][9]+dp[i][j-1]; 然后注意下对于每个询问统计的时候如果当前位为1需要额外加上他后面所有位数的个数,就是n%pow(10,i-1); 这样总复杂度log(n)*10 #include <bits/stdc++.