51nod 1623:完美消除

基準時間限制:1 秒 空間限制:131072 KB 分值: 80 難度:5級算法題
 收藏
 關注

定義數的消除操作爲選定[L,R,x],如果數的第L到第R位上的數字都大於等於x,並且這些數都相等,那麼該操作是合法的(從低位到高位編號,個位是第一位,百位是第二位……),然後將這些位數上的數減x;否則就是不合法的,不能進行操作。對一個數操作最少的次數使得這個數變成0,這個操作次數稱爲該數的最小操作數。如:1232的最小操作數爲3,一個合法解是[2,2,1],[1,3,2],[4,4,1]。

求L~R中最小操作數爲k的數的個數。


例如:132,需要操作3次才能變爲0。而131131 => 111131 => 111111 =>0

Input
單組測試數據。
三個整數L、R和k(1<=L<=R<=10^18,1<=k<=18)。
Output
一個整數表示答案。
Input示例
10 21 2
Output示例
9

明顯的數位dp,但是做的時候不知道如何求1到x裏面符合要求的個數有多少個。

下面是官方題解:(表示維護一個棧的思路簡直了,又被自己低智商給感動了)

對於一個數計算最小操作數:
維護一個棧。
從高位到低位依次考慮每一位,設當前數字爲x,將棧裏所有大於x的數字刪除,如果此時棧裏沒有數字x則加入,並且答案+1。
用一個二進制數來表示棧裏有哪些元素。
因爲可以按位考慮所以可以用數位dp來做這個問題。
最後答案等於1~R裏符合要求的數量-1~L-1裏符合要求的數量。所以這裏只考慮1~R的計算。
設數字R有n位,a[i]表示第i位。(從低位到高位編號,個位是第一位,百位是第二位……)
設某數第i位的數字爲x,且x<a[i],且第i+1位~第n位數字與R相同,將這個數記爲(i,x),
可以通過這個標準將數分類,最多18*9種分類。
對於一種分類,統計這種分類裏有幾個數符合題目要求。
計算出此時棧的狀態和已累加的答案(即最小操作數),記爲 t1,t2 , dp[t1][i−1][K−t2] 爲這類裏符合要求的數的數量(K爲輸入要求的最小操作數)。
dp[i][j][k] 表示當前棧裏的元素狀態爲i,再填j位數字,使答案(即最小操作數)剛好再加k的方案數,這個可以通過枚舉下一位數字轉移得到。
總複雜度O(18*512*18*10)

代碼:

//#pragma comment(linker, "/STACK:102400000,102400000") 
#pragma warning(disable:4996)
#include <fstream>
#include <iostream>
#include <functional>
#include <algorithm>
#include <cstring>
#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <deque>
#include <ctime>
#include <set>
#include <map>
using namespace std;
typedef long long ll;

#define eps 1e-10
#define LL_INF 0x3fffffffffffffff
#define INF 0x3f3f3f3f
#define mem(a, b) memset(a, b, sizeof(a))
#define pper(i,n,m) for(int i = n;i >= m; i--)
#define repp(i, n, m) for (int i = n; i <= m; i++)
#define rep(i, n, m) for (int i = n; i < m; i++)
#define sa(n) scanf("%d", &(n))
#define mp make_pair
#define ff first
#define ss second
#define pb push_back

const int maxn = 8005;
const ll mod = 1e9 + 7;
const double PI = acos(-1.0);

ll L, R, k;
ll res[20][20][1050];
int dig[20];

int change(int s, int x)
{
	int i, j, k;
	for (i = 0; i <= 9; i++)
	{
		if ((i > x) && (s >> i & 1))
		{
			s ^= (1 << i);
		}
	}
	return s | (1 << x);
}

ll dfs(int len, int num, int sta,int up)
{
	if (len == 0)
	{
		return num == k;
	}
	if (!up&&res[len][num][sta] != -1)
	{
		return res[len][num][sta];
	}
	int u = up == 1 ? dig[len] : 9;
	int i, j, k;
	ll ans = 0;
	for (i = 0; i <= u; i++)
	{
		if (sta >> i & 1)
		{
			ans += dfs(len - 1, num, change(sta, i), i == u&&up);
		}
		else
		{
			ans += dfs(len - 1, num + 1, change(sta, i), i == u&&up);
		}
	}
	if (!up)
		res[len][num][sta] = ans;
	return ans;
}

ll solve(ll x)
{
	mem(res, 0);
	int len = 0;
	while (x)
	{
		len++;
		dig[len] = x % 10;
		x /= 10;
	}
	mem(res, -1);
	return dfs(len, 0, 1, 1);
}

int main()
{
	cin >> L >> R >> k;
	cout << solve(R) - solve(L - 1) << endl;
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章