题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3662

之前写过这道题,结果被康神吐槽说代码写的挫。

的确,那时候想法太挫了。

题意:

给你三个数n,m,k。

问你存在多少个数列 a1,a2,...,ak,使得他们的和为n,他们的最小公倍数为m。

想法一:

因为 lcm(a1,a2,...,ak)=m,所以a1,a2,a3,...,ak都是m的约数。

因此预处理出来m的约数,记在w[i]里。

设计状态dp[i][j][k]代表有i个数的数列,和为j,最小公倍数为k的个数。

转移:dp[i][j][k] = sigma( dp[i-1][s][t] ) 其中 s+w[i] = j,lcm( t,w[i] ) = k

于是写成 dp[i][s+w[i]][lcm(t,w[i])] = sigma( dp[i-1][s][t] )

需要优化的部分较多,需要滚动数组,很容易TLE掉。

时间复杂度O( n*m*k*sqrt(n) )

于是被康神吐槽了。。

 #include <bits/stdc++.h>
using namespace std; const int MOD = ;
int n,m,kk;
int a[],ptr;
int lcmn[][];
int dp[][][];
int ss[][][]; void getlcmn(){
for(int i=;i<;i++){
for(int j=;j<;j++){
lcmn[i][j] = i / __gcd(i,j) * j;
}
}
} int main(){
getlcmn();
while(scanf("%d%d%d",&n,&m,&kk)!=EOF){
ptr = ;
for(int i=;i*i<=m;i++){
if( m%i== ){
a[ptr++] = i;
if( i*i!=m ) a[ptr++] = m/i;
}
}
//for(int i=0;i<ptr;i++) printf("%d ",a[i]); puts("");
memset(dp,,sizeof(dp));
for(int i=;i<=min(m,n);i++) dp[][i][i] = ;
for(int i=;i<=kk;i++){
for(int j=;j<=n;j++){
for(int k=;k<=m;k++) dp[i&][j][k] = ;
for(int s=;s<ptr;s++){
if( j-a[s]>= ){
for(int t=;t<ptr;t++) {
if(lcmn[a[s]][a[t]]<=m) {
dp[i&][j][lcmn[a[s]][a[t]]] += dp[(i-)&][j-a[s]][a[t]];
dp[i&][j][lcmn[a[s]][a[t]]] %= MOD;
}
}
}
}
}
}
printf("%d\n",dp[kk&][n][m]);
}
return ;
}

想法一

当时听康神说所谓的正解,还真是没听懂,正巧昨天做了这道题:http://www.cnblogs.com/llkpersonal/p/4037686.html

于是今天早上就想起这个题目,也想了想“正解”。

由题意我们知道,m是a1,a2,...,ak的最小公倍数,因此,将a1,a2,a3,...,an质因数分解,取每一个质因数的最高次幂,然后乘起来就是m。

那么我们先对m进行质因数分解,然后划分状态,假设有sn个质因数,那么总状态数就是(1<<sn)-1个。

然后去分解m的因数,给每个因数定状态,如果说因数k的质因数的指数等于m的指数,那么就给该状态标1。记作v[i]

然后去跑完全背包。

定义状态dp[i][j][k][mask]代表数列有i个数,从前j个数里选,和为k,状态为mask。

状态转移:dp[i][j][k][mask] = sigma( dp[i-1][j-1][s][t] ) 其中s+w[i] = k , t|v[i]=mask

于是有dp[i][j][s+w[i]][t|v[i]] = sigma( dp[i-1][j-1][s][t] ) 对于每一个i,都相当于是一次独立的完全背包。

由于是完全背包,因此j维可以省掉。

于是化为

dp[j][s+w[i]][t|v[i]] = sigma( dp[j-1][s][t|v[i]] )

正向递推

 #include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <iterator>
#include <vector>
using namespace std;
typedef long long LL; int n,m,k;
int p[],h[],w[],v[],ptr;
int dp[][][<<];
const int MOD = ; void prime_factor(int x){
for(int i=;i*i<=x;i++){
while( x%i== ){
p[i]++;
x /= i;
}
}
if( x!= ) p[x] = ;
} void handle(int x){
w[++ptr] = x;
for(int i=;i*i<=x;i++){
int t = ;
while( x%i== ){
t ++;
x /= i;
}
if( t&&t==p[i] ) v[ptr] |= (<<(h[i]-));
}
if( x!=&&p[x]== ) v[ptr] |= (<<(h[x]-));
} int main(){
while(scanf("%d%d%d",&n,&m,&k)!=EOF){
ptr = ;
memset(p,,sizeof(p));
memset(w,,sizeof(w));
memset(v,,sizeof(v));
memset(h,,sizeof(h));
prime_factor(m);
int sn = ;
for(int i=;i<;i++){
if( p[i] ) {
h[i] = ++sn;
}
}
for(int i=;i*i<=m;i++){
if( m%i== ) {
handle(i);
if( i*i!=m ) handle(m/i);
}
}
memset(dp,,sizeof(dp));
dp[][][] = ;
for(int j=;j<=k;j++){
for(int i=;i<=ptr;i++){
for(int k=;k<=n;k++){
for(int mask=;mask<(<<sn);mask++){
if( k+w[i]<=n&&(mask|v[i])<(<<sn) ){
dp[j][k+w[i]][mask|v[i]] = (dp[j][k+w[i]][mask|v[i]]+dp[j-][k][mask])%MOD;
}
}
}
}
}
printf("%d\n",dp[k][n][(<<sn)-]);
}
return ;
}

想法二

[ZOJ 3662] Math Magic (动态规划+状态压缩)的更多相关文章

  1. ZOJ 2563 Long Dominoes(状态压缩DP)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1563 题目大意:在h*w的矩阵里铺满1*3的小矩阵,共有多少种方法 ...

  2. [动态规划]状态压缩DP小结

     1.小技巧 枚举集合S的子集:for(int i = S; i > 0; i=(i-1)&S) 枚举包含S的集合:for(int i = S; i < (1<<n); ...

  3. [POJ 2923] Relocation (动态规划 状态压缩)

    题目链接:http://poj.org/problem?id=2923 题目的大概意思是,有两辆车a和b,a车的最大承重为A,b车的最大承重为B.有n个家具需要从一个地方搬运到另一个地方,两辆车同时开 ...

  4. POJ 1185 炮兵阵地(动态规划+状态压缩)

    炮兵阵地 Description 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原( ...

  5. ACM学习历程—HDU1584 蜘蛛牌(动态规划 && 状态压缩 || 区间DP)

    Description 蜘蛛牌是windows xp操作系统自带的一款纸牌游戏,游戏规则是这样的:只能将牌拖到比她大一的牌上面(A最小,K最大),如果拖动的牌上有按顺序排好的牌时,那么这些牌也跟着一起 ...

  6. HDOJ-1074(动态规划+状态压缩)

    Doing Homework HDOJ-1074 1.本题主要用的是状态压缩的方法,将每种状态用二进制压缩表示 2.状态转移方程:dp[i|(1<<j)]=min(dp[i|(1<& ...

  7. zoj 3471 Most Powerful(状态压缩dp)

    Recently, researchers on Mars have discovered N powerful atoms. All of them are different. These ato ...

  8. 动态规划状态压缩-poj1143

    题目链接:http://poj.org/problem?id=1143 题目描述: 代码实现: #include <iostream> #include <string.h> ...

  9. 3.4 熟练掌握动态规划——状态压缩DP

    从旅行商问题说起—— 给定一个图,n个节点(n<=15),求从a节点出发,经历每个节点仅一次,最后回到a,需要的最短时间. 分析: 设定状态S代表当前已经走过的城市的集合,显然,S<=(1 ...

随机推荐

  1. Amoeba:开源的分布式数据库Porxy解决方案

    http://www.biaodianfu.com/amoeba.html 什么是Amoeba? Amoeba(变形虫)项目,该开源框架于2008年 开始发布一款 Amoeba for Mysql软件 ...

  2. eclipse luna maven搭建spring mvc

    1. 环境配置 a)         Java 1.7 b)         Eclipse luna c)         Maven3.2.5 d)         Spring 4.1.4 2. ...

  3. fastjson 使用方法

    Fastjson介绍 Fastjson是一个Java语言编写的JSON处理器. 1.遵循http://json.org标准,为其官方网站收录的参考实现之一. 2.功能qiang打,支持JDK的各种类型 ...

  4. sql2008读取excel

    环境:win7(64位)+sql2008 sql语句: --启用Ad Hoc Distributed Queries: reconfigure reconfigure --使用完成后,关闭Ad Hoc ...

  5. [原]对Linux环境下任务调度一点认识

    我一直以来有一个误解,那就是在终端运行某个程序时,按下Ctrl + D时我误以为就是杀死了这个进程,今天才知道原来不是.比如我利用libevent在Linux环境下写了一个网络监听程序,当启动程序之后 ...

  6. [原]在Fedora 20环境下安装系统内核源代码

    1.安装Kernel Headers(头文件) 通过安装kernel-devel RPM包就可以得到Kernel Headers,但默认情况下没有被Fedora 20安装.通过DVD ISO 或者 y ...

  7. 剑指offer系列55---最小的k个数

    [题目] 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. *[思路]排序,去除k后的数. package com.exe11 ...

  8. ios的runtime为什么可以动态添加方法

    一句话概括 每个instance都有一个isa,这个isa,里面含有所有的方法列表,ios提供库函数增加,修改,即实现了动态添加方法

  9. 利用MyEclipse自动创建PO类、hbm文件(映射文件)、DAO

    原文地址:http://blog.csdn.net/fangzhibin4712/article/details/7179414 前提条件:表sjzdfl  表sjzdxx (使用数据库MySQL) ...

  10. 3D视觉差---原生js+css

    <!doctype html> <html> <head> <meta http-equiv="Content-Type" content ...