{POJ}{3971}{Scales}{O(N)動態規劃}

題意:給定一堆2二進制砝碼,給定一個物品,要求在天平兩端加入物品和砝碼使之平衡,求可能數。ios

思路:一開始想到了直接用數學原理,結果沒證出來。作以下思考,此題須要用二進制:spa

(1)設物品重量爲w,加入的砝碼重量爲x,另外一邊重量爲y,便有w+x=ycode

(2)另外,假如物品爲100110,加入的砝碼能夠爲000010,那麼總和爲101000,顯然x與y不能有位數相同的1(由於每種砝碼只有一個),所以便有x&y=0blog

依據這兩點,能夠知道此題的關鍵之處就在於如何分析w+x的進位狀況。分析物品的第i位,好比爲1,那麼若是前面的一位沒有進位,那麼他即可以加上1或者不加;若是進位,那就確定不能加1(由於加1之後與進位的1加上這一位的結果仍是1,與x&y=0矛盾),因此對於每一位,它的進位與不進位狀況須要分開判斷。get

DP思路:設f[i][0]表示判斷到i位時它不進位的狀況數,f[i][1]表示到i位時它進位的狀況數,都是從低位到高位判斷。數學

(1)先考慮f[i][0](不進位的狀況)string

  • i位爲1,若是前面不進位,那麼這一位只能加上0才知足不進位的狀況;若是前面進位,那麼不管如何不可能使這一位不進位,所以f[i][0]=f[i-1][0];
  • i位爲0,若是前面不進位,那麼這一位只能選擇0(由於選擇加1的話將會與x&y=0矛盾);若是前面進位,那麼這一位也只能選擇0才能使i位也不進位,所以有f[i][0]=f[i-1][0]+f[i-1][1];

(2)再考慮f[i][1](進位的狀況)it

  • i位爲1,若是前面不進位,那麼這一位只能選擇1才能使i位進位;若是前面進位,那麼只能選擇0使之進位(若是選擇1那麼結果將會與x&y=0矛盾),所以f[i][1]=f[i-1][0]+f[i-1][1];
  • i位爲0,若是前面不進位,那麼這一位不管如何不可能進位;若是前面進位,那麼只能選擇1才能使i位進位,所以f[i][1]=f[i-1][1];

這樣,DP方程就獲得了io

if(s[i] == 1){
    f[i][0] = f[i-1][0];
    f[i][1] = f[i-1][0]+f[i-1][1];
}
else{
    f[i][0] = f[i-1][0]+f[i-1][1];
    f[i][1] = f[i-1][1];
}

注意:不得不說,這個題目挺不錯的,此題中間結果可能會超出long long,所以須要分次數判斷,由於這個點我WA了N次class

#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <memory>
#include <cmath>
#include <bitset>
#include <queue>
#include <vector>
#include <stack>
using namespace std;
 

#define CLR(x,y) memset(x,y,sizeof(x))
#define MIN(m,v) (m)<(v)?(m):(v)
#define MAX(m,v) (m)>(v)?(m):(v)
#define ABS(x) ((x)>0?(x):-(x))
#define rep(i,x,y) for(i=x;i<y;++i)

const int MAXN = 1100000;

int t,n,m,d;
int s[MAXN];
int dp[MAXN][2];

void Solve()
{
	char c;

	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d%d",&n,&m,&d);
		getchar();
		CLR(s,0);
		for(int i = m-1; i >= 0; --i){
			scanf("%c",&c);
			s[i] = c-'0';
		}
		if(s[0] == 0) {
			dp[0][0] = 1;
			dp[0][1] = 0;
		}
		else{
			dp[0][0] = 1;
			dp[0][1] = 1;
		}
		for(int i = 1; i < n; ++i){
			if(s[i] == 1){
				dp[i][0] = dp[i-1][0];
				dp[i][1] = dp[i-1][0]+dp[i-1][1];
			}
			else{
				dp[i][0] = dp[i-1][0]+dp[i-1][1];
				dp[i][1] = dp[i-1][1];
			}
			if(dp[i][0]>=d) dp[i][0]-=d;
			if(dp[i][1]>=d) dp[i][1]-=d;
		}
		cout<<(dp[n-1][0])<<endl;
	}
}


int main()
{
	Solve();
	return 0;
}
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息