HDU 5456
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5456
题意:
给n根火柴,用火柴来拼数字,每个数字需要不同的火柴。
问,最多能产生多少个样子为“a - b = c”的式子,其中a、b、c均为正数
思路:
首先感谢这位博主http://blog.csdn.net/playwfun/article/details/48579083,基本是照着他的改的。
数位dp。
从低位向高位开始增加数字给a、b,并算出c。容易发现,不论低位的运算如何,对高位的影响最多为一个进位1,所以只要存储这个进位就行。
设dp[x][y][s],x表示当前剩余火柴数,y表示之前等式的进位(范围0-1),s用二进制表示a、b、c三个数是否已经不在增加,用来去除首0(范围0-7)。然后每次穷举a和b在高位增加的新数字,并用状态s来去除不合法的情况(比如a已经不能用了又给他加了个大于0的数之类),然后算下代价,枚举新的代价ts。
用了记忆化搜索的优化。
自己想的时候纠结于怎么去记录之前的a和b,然后由于加法的特性并不需要记录。其实就是不会数位dp……
源码:
using namespace std;
const int MAXN = 500 + 5;
int n, m;
int cost[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
int vis[MAXN][2][8];
LL dp[MAXN][2][8];
LL dfs(int x, int y, int s)
{
if(vis[x][y][s]) return dp[x][y][s];
vis[x][y][s] = 1;
if(x == 0){
// printf(“second\n”);
if(y == 0 && s == 7)
return dp[x][y][s] = 1;
else
return dp[x][y][s] = 0;
}
dp[x][y][s] = 0;
for(int i = 0 ; i < 10 ; i++){
for(int j = 0 ; j < 10 ; j++){
int carry = (i - y) >= j ? 0 : 1;
int a = i, b = j, c = carry * 10 + i - j - y;
if((s&1) && c != 0) continue;
if((s&2) && b != 0) continue;
int tcost = cost[a] + ((s&1) ? 0 : cost[c]) + ((s&2) ? 0 : cost[b]);
// if(x == 9 && i == 2 && j == 1){
// printf(“carry = %d, a = %d, b = %d, c = %d, tcost = %d\n”, carry, a, b, c, tcost);
// printf(“cost[a] = %d, s = %d\n”, cost[a], s);
// }
if(tcost > x) continue;
int tvis[8] = {0};
for(int k = 0 ; k < 8 ; k++){
int ts = 0;
if((k&1) && c!=0) ts |= 1;
if((k&2) && b!=0) ts |= 2;
if((k&4) && a!=0 && tcost == x && carry == 0) ts |= 4;
if(!tvis[ts]) dp[x][y][s] = (dp[x][y][s] + dfs(x - tcost, carry, ts|s)) % m, tvis[ts] = 1;
}
}
}
// printf(“x = %d, y = %d, s = %d, dp = %lld\n”, x, y, s, dp[x][y][s]);
// system(“pause”);
return dp[x][y][s];
}
int main()
{
// freopen(“1007.in”, “r”, stdin);
int t;
scanf(“%d”, &t);
for(int cas = 1 ; cas <= t ; cas++){
scanf(“%d%d”, &n, &m);
memset(vis, 0, sizeof(vis));
printf(“Case #%d: %I64d\n”, cas, dfs(n - 3, 0, 0) % m);
// printf(“dp\n
// for(int j = 0 ; j < 2 ; j++){
// for(int k = 0 ; k < 8 ; k++)
// printf(“dp[%d][%d][%d] = %lld\n”, i, j, k, dp[i][j][k]);
// }
// }
// printf(“dp\n”);
}
return 0;
}