codeforces 626F DP 如何设置状态,方便转移
2016-03-05 11:32
225 查看
想到用动态规划来做,但是考虑到第i个数的时候,前面数的分组情况并不知道,开始准备用类似并查集的方法,但是也不好保存
能够利用的性质是只和最大最小有关,所以想到排序之后解决,但是还是不知道应该怎么扩展状态。
看了题解,DP[I][J][K]表示考虑到第i个的时候,总共还有j组没有关闭,所需的费用,每次扩展状态的时候给所有的j加上a[i] - a[i-1] 这样以后结束的时候,加上去就可以了,只要考虑有多少组要加上这个,就可以得到答案,还有一点需要注意的是,第i个可以一个人构成一个闭组,或者一个人构成一个开组,或者和前面的组成闭组,或者和前面的组成开组,和前面的组的时候,记得乘以可能的组数,因为并到任何一组都是不同的,真是好题……想不到啊
上搞了半天才AC而且时间有点长,有点丑的代码
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 205;
const int maxk = 1000;
const int M = 1000000007;
int n,k;
int a[maxn];
int dp[maxn][maxn + 1][maxk + 1];
int main()
{
cin >> n >> k;
for(int i = 0; i < n; i++)
cin >> a[i];
sort(a,a + n);
memset(dp,0,sizeof(dp));
dp[0][0][0] = 1;
dp[0][1][0] = 1;
for(int i = 1; i < n; i++)
for(int j = 0; j <= i + 1; j++)
for(int p = 0; p <= k; p++){
int t;
if ((t = p - (a[i] - a[i - 1]) * j) >= 0 )
dp[i][j][p] += dp[i - 1][j][t];//单独一个,闭了,对前面j个没闭的有影响
if (j >= 1 && (t = p - (a[i] - a[i - 1]) * (j- 1)) >= 0) dp[i][j][p] = (dp[i][j][p] + dp[i - 1][j - 1][t]) % M;//单独一个,不闭 ,对前面J - 1个没闭的有影响
if ((a[i] - a[i - 1]) * (j + 1) <= p && j + 1 <= n) //和前面的某个闭
dp[i][j][p] = (dp[i][j][p] + (long long) (j + 1) * dp[i - 1][j + 1][p - (a[i] - a[i - 1])*(j + 1)] % M) %M;;
if ((a[i] - a[i - 1]) * j <= p && j != 0)//和前面某个在一起,不闭
dp[i][j][p] = (dp[i][j][p] + (long long )j * dp[i - 1][j][p - (a[i] - a[i - 1])*j] % M) % M;
//printf("%d %d %d %d\n",i,j,p,dp[i][j][p]);
}
int ans = 0;
for(int p = 0; p <= k; p++)
ans = (ans + dp[n - 1][0][p]) %M;
cout << ans << '\n';
return 0;
}
能够利用的性质是只和最大最小有关,所以想到排序之后解决,但是还是不知道应该怎么扩展状态。
看了题解,DP[I][J][K]表示考虑到第i个的时候,总共还有j组没有关闭,所需的费用,每次扩展状态的时候给所有的j加上a[i] - a[i-1] 这样以后结束的时候,加上去就可以了,只要考虑有多少组要加上这个,就可以得到答案,还有一点需要注意的是,第i个可以一个人构成一个闭组,或者一个人构成一个开组,或者和前面的组成闭组,或者和前面的组成开组,和前面的组的时候,记得乘以可能的组数,因为并到任何一组都是不同的,真是好题……想不到啊
上搞了半天才AC而且时间有点长,有点丑的代码
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 205;
const int maxk = 1000;
const int M = 1000000007;
int n,k;
int a[maxn];
int dp[maxn][maxn + 1][maxk + 1];
int main()
{
cin >> n >> k;
for(int i = 0; i < n; i++)
cin >> a[i];
sort(a,a + n);
memset(dp,0,sizeof(dp));
dp[0][0][0] = 1;
dp[0][1][0] = 1;
for(int i = 1; i < n; i++)
for(int j = 0; j <= i + 1; j++)
for(int p = 0; p <= k; p++){
int t;
if ((t = p - (a[i] - a[i - 1]) * j) >= 0 )
dp[i][j][p] += dp[i - 1][j][t];//单独一个,闭了,对前面j个没闭的有影响
if (j >= 1 && (t = p - (a[i] - a[i - 1]) * (j- 1)) >= 0) dp[i][j][p] = (dp[i][j][p] + dp[i - 1][j - 1][t]) % M;//单独一个,不闭 ,对前面J - 1个没闭的有影响
if ((a[i] - a[i - 1]) * (j + 1) <= p && j + 1 <= n) //和前面的某个闭
dp[i][j][p] = (dp[i][j][p] + (long long) (j + 1) * dp[i - 1][j + 1][p - (a[i] - a[i - 1])*(j + 1)] % M) %M;;
if ((a[i] - a[i - 1]) * j <= p && j != 0)//和前面某个在一起,不闭
dp[i][j][p] = (dp[i][j][p] + (long long )j * dp[i - 1][j][p - (a[i] - a[i - 1])*j] % M) % M;
//printf("%d %d %d %d\n",i,j,p,dp[i][j][p]);
}
int ans = 0;
for(int p = 0; p <= k; p++)
ans = (ans + dp[n - 1][0][p]) %M;
cout << ans << '\n';
return 0;
}
相关文章推荐
- 书评:《算法之美( Algorithms to Live By )》
- 动易2006序列号破解算法公布
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法
- Ruby实现的合并排序算法
- C#折半插入排序算法实现方法
- C++动态规划之最长公子序列实例
- C++动态规划之背包问题解决方法
- 基于C++实现的各种内部排序算法汇总
- C++线性时间的排序算法分析