POJ3971 Scales

本文探讨了给定物品重量和一系列2的整次幂砝码时,如何通过数位DP算法计算天平平衡的方法数量。分析了在特定条件下砝码放置的合法正整数解,并详细解释了DP状态转移方程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

有一个物品重量为www,现在你有1,2,4,…,2n1,2,4,…,2^n1,2,4,,2n重量的砝码,问有多少种方法可以使得天平平衡。www以二进制给出。

分析:

  • 根据题目的意思,可以分析出2个重要的信息,假设在www方托盘放上xxx质量的砝码,在天平的另一边放上总共yyy质量的砝码,则有等式x+w=yx+w=yx+w=y
  • 另外,因为每一种砝码只有一个,且每一个砝码都是222的整次幂,化成222进制后只有最高位为111,而其余位为000,因此又有以下等式成立: x&y=0x \& y = 0x&y=0
    综上所述,我们需要求解以下方程组的合法正整数解的个数:
    {x+w=yx&y=0\begin{cases} x +w = y \\ x \& y = 0 \end{cases}{x+w=yx&y=0
    考虑数位dpdpdp
    fi,0f_{i,0}fi,0表示判断到i位时它不进位的情况数,fi,1f_{i,1}fi,1表示到i位时它进位的情况数,从低位到高位判断。
    分类讨论:

(111) 先考虑fi,0f_{i,0}fi,0的情况(即不进位的情况):

  • 1.1.1.www的第iii位为111时,如果前面不进位,那么这一位只能加上000才满足不进位的情况;如果前面进位,那么无论如何不可能使这一位不进位,因此有方程fi,0=fi−1,0f_{i,0}=f_{i-1,0}fi,0=fi1,0成立;
  • 2.2.2.www的第iii位为000时,如果前面不进位,那么这一位只能选择000(因为选择加111的话将会与条件x&y=0x\&y=0x&y=0矛盾);如果前面进位,那么这一位也只能选择000才能使iii位也不进位,因此有方程fi,0=fi−1,0+fi−1,1f_{i,0}=f_{i-1,0}+f_{i-1,1}fi,0=fi1,0+fi1,1成立;

(222) 再考虑fi,1f_{i,1}fi,1的情况(即进位的情况):

  • www的第iii位为111时,如果前面不进位,那么这一位只能选择1才能使i位进位;如果前面进位,那么只能选择000使之进位(如果选择111那么结果将会与条件x&y=0x\&y=0x&y=0矛盾),因此有方程fi,1=fi−1,0+fi−1,1f_{i,1}=f_{i-1,0}+f_{i-1,1}fi,1=fi1,0+fi1,1成立;
  • www的第iii位为000时,如果前面不进位,那么这一位无论如何不可能进位;如果前面进位,那么只能选择111才能使i位进位,因此有方程fi,1=fi−1,1f_{i,1}=f_{i-1,1}fi,1=fi1,1成立;

DpDpDp方程转移到此结束.
程序初始化与上述分析类似.

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 1000001;
int n, m, Mod = 0;
int a[N] = {};
int f[N][2] = {};

int main() {
	int T;
	scanf("%d",&T);
	while (T --) {
		memset(a, 0, sizeof(a));
		memset(f, 0, sizeof(f));
		scanf("%d%d%d",&m,&n,&Mod);
		char ch;
		ch = getchar();
		for (int i=n-1;i>=0;i--) {
			ch = getchar();
			a[i] = ch - '0';
		}
//		for (int i=0;i<=n-1;i++) cout<<a[i]<<' ';
		if (a[0] == 0) {
			f[0][0] = 1, f[0][1] = 0;
		}
		else {
			f[0][0] = f[0][1] = 1;
		}
		for (int i=1;i<=m;i++) {
			if (a[i] == 0) {
				f[i][0] = f[i-1][0] + f[i-1][1];
				f[i][1] = f[i-1][1];
			}
			else {
				f[i][0] = f[i-1][0];
				f[i][1] = f[i-1][0] + f[i-1][1];
			}
			f[i][0] = f[i][0] % Mod, f[i][1] = f[i][1] % Mod;
		}
		printf("%d\n",f[m-1][0]);
	}
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值