dp

HDOJ(HDU).1114 Piggy-Bank (DP 完全背包)

江枫思渺然 提交于 2019-12-04 13:50:45
HDOJ(HDU).1114 Piggy-Bank (DP 完全背包) 题意分析 裸的完全背包 代码总览 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define nmax 100000 #define INF 0x3f3f3f3f using namespace std ; int we[nmax],va[nmax],dp[nmax]; struct item{ int w; int v; }arr[nmax]; int main() { //freopen("in.txt","r",stdin); int t; scanf ( "%d" ,&t); while (t--){ int e,f; scanf ( "%d%d" ,&e,&f); int n; scanf ( "%d" ,&n); memset (arr, 0 , sizeof (arr)); memset (dp,INF, sizeof (dp)); for ( int i = 1 ; i<=n; ++i){ scanf ( "%d%d" ,&arr[i].v,&arr[i].w); } dp[ 0 ] = 0 ; for ( int i = 1 ;i<=n; ++i){ for ( int j =

Codeforces813DTwo Melodies(dp)

三世轮回 提交于 2019-12-04 07:03:25
链接 题意: 给定一个长度为n的序列,需要取出两个不相交的子序列,每个子序列满足相邻两个数相差一或者求余7相等。求满足条件的两个不相交的子序列长度的和最大可以是多少(n<=5000,a[i]<=1e5) 分析: 考虑dp的做法,dp[i][j]表示子序列1以a[i]结尾,子序列2以a[j]结尾时能得到的最大值。从0开始枚举i(0就代表只有一个序列),则有dp[i][j]=max(dp[i][k]+1,dp[i][j])(k<j,a[k]≡a[j]mod7||abs(a[k]-a[j])=1) 注意在枚举j时需要从i+1开始枚举这样可以避免序列1和序列2选到同一个元素。这样的做法是O(n^3),显然会T,由于a[i]<=1e5,所以可以用1e5的数组存放值为a[k]时dp[i][k]的最大值,因此可以将复杂度降为O(n*1e5). 代码: #include <bits/stdc++.h> using namespace std; const int maxn=5005; int a[maxn]; int dp[maxn][maxn]; int val[100005]; int mod[8]; int main() { int n; scanf("%d",&n); for(int i = 1;i <= n;++i)scanf("%d",&a[i]); int ans=0; for

奶牛抗议 DP 树状数组

老子叫甜甜 提交于 2019-12-04 06:53:28
奶牛抗议 DP 树状数组 USACO的题太猛了 容易想到 \(DP\) ,设 \(f[i]\) 表示为在第 \(i\) 位时方案数,转移方程: \[ f[i]=\sum f[j]\;(j< i,sum[i]-sum[j]\ge0) \] \(O(n^2)\) 过不了,考虑优化 移项得: \[ f[i]=\sum f[j]\;(j< i,sum[i]\ge sum[j]) \] 这时候我们发现相当于求在 \(i\) 前面并且前缀和小于 \(sum[i]\) 的所有和,这就可以用一个树状数组优化了,在树状数组维护下标为 \(sum[i]\) , \(f[i]\) 的前缀和。对于每个 \(f[i]\) 即为树状数组上 \(sum[i]\) 的前缀和。 这里需要注意的是前缀和可能为负,而树状数组下标不能为负,所以我们要离散化一下。 #include <cstdio> #include <algorithm> using namespace std; #define MAXN 100010 #define lowbit(x) ((x)&(-(x))) #define MOD 1000000009 int n,sum[MAXN],s; int sum_sort[MAXN+1]; int tre[MAXN+1]; inline void add(int x, int val){ while(x<

[题解]HDU4035 Maze

…衆ロ難τιáo~ 提交于 2019-12-04 04:28:50
传 题目描述 一棵 \(n\) 个节点的树,从1号结点开始游戏,在每一个点 \(x\) : 有 \(a[x]/100\) 的可能掉进陷阱死翘翘回到1重新开始 有 \(b[x]/100\) 的可能找到出口并结束游戏 剩下的可能中,你等概率随机选一条和它相连的边(可以是父亲)走过去 问期望多少步结束游戏 \(1\leq T \leq 30,1\leq n \leq 10000\) 分析 设 \(dp[x]\) 表示位于 \(x\) 节点时,期望走几步才能结束游戏 那么 \(dp[x]=0.01a[x]*dp[1]+0.01b[x]*0+0.01(1-a[x]-b[x])*(\frac{1}{cnt}(\sum dp[son]+dp[1]+1))\) 看了一下数据范围发现并不能承受高斯消元的时间复杂度 我们发现,每一个点的 \(dp\) 值之和父亲、1号点还有儿子有关,那么叶子节点的 \(dp\) 值可以用父亲和1号点表示,这样往上代,合并同类项之后可以发现,每一个点的值都可以表示为: \(dp[x]=...*dp[1]+...*dp[fa[x]]+...\) (常数项) 所以只需要维护一下每一项的系数就可以 \(O(n)\) 求解了,记得特判 Impossible 代码 void SEARCH(int x,int pa){ f[x][0]=f[x][1]=f[x][2]=0; int

树形dp

谁说胖子不能爱 提交于 2019-12-04 02:34:51
还是很想引用lyd《算法竞赛进阶指南》总的几句话。 在树上设计动态规划时,一般就以节点从深到浅( 子树由大到小 )的顺序作为dp的阶段。 在方程中一般 第一维是节点编号 ,代表以该节点为根的子树。 大多数情况下都采用 递归 的形式实现树形dp。 对于每个节点x, 先递归它的每个子节点 进行dp,在 回溯 时,从子节点向节点x进行状态转移。 举个栗子: 一个n个节点的树,每个点有权值V i, 根节点与子节点不能同时取,求总权值的最大值。 1 #include<iostream> 2 #include<vector> 3 using namespace std; 4 int n,f[7000][3]; 5 int vis[7000],h[7000]; 6 vector<int>son[7000]; 7 void dp(int x) 8 { 9 f[x][1]=h[x]; 10 f[x][0]=0; 11 for(int i=0;i<son[x].size();i++) 12 { 13 int y=son[x][i]; 14 dp(y); 15 f[x][0]+=max(f[y][1],f[y][0]); 16 f[x][1]+=f[y][0]; 17 } 18 } 19 int main() 20 { 21 int x,y; 22 cin>>n; 23 for(int i=1;i<=n

动态规划训练之二十

大憨熊 提交于 2019-12-03 20:44:31
https://www.luogu.org/problem/P1370 题目大意:Σ(1<=l<=r<=n)F(l, r),其中F(l,r)表示[l,r]之间的本质不同的子序列有多少个 分析: 如果本题数据是N^2^的话我就会做,但是要求是O(n)求出 同样的 区间计数题 , 固定端点L ,再 考虑dp 设dp[i]表示F[i,i]+F[i,i+1]+F[i,i+2]+....+F[i,n] 如果没有重复的数字话 dp[i]=(dp[i+1]<<1)+2 意思是: dp[i+1]= F[i+1,i+1]+F[i+1,i+2]+F[i+1,i+3]+....+F[i+1,n] dp[i]= F[i,i]+ F[i,i+1]+ F[i,i+2]+ F[i,i+3] +....+ F[i,n] 相信一看就懂把 但是有重复怎么办? 若ai==aj(i<j) 则dp[i]-=dp[j+1]+1; 原因是 后面所有的F[j+1,j+1].....都会有一次重复(接ai,接aj) 那个1就是F[j,j]中选j或不选j中的选j的方案 code(代码懒得打了): #include<bits/stdc++.h> using namespace std; #define ll long long #define mod 998244353 #ifdef ONLINE_JUDGE char *TT,*mo

2037: [Sdoi2008]Sue的小球(区间DP)

旧街凉风 提交于 2019-12-03 20:21:50
2037: [Sdoi2008]Sue的小球 Time Limit: 10 Sec Memory Limit: 64 MB Submit: 945 Solved: 538 [ Submit ][ Status ][ Discuss ] Description Sue和Sandy最近迷上了一个电脑游戏,这个游戏的故事发在美丽神秘并且充满刺激的大海上,Sue有一支轻便小巧的小船。然而,Sue的目标并不是当一个海盗,而是要收集空中漂浮的彩蛋,Sue有一个秘密武器,只要她将小船划到一个彩蛋的正下方,然后使用秘密武器便可以在瞬间收集到这个彩蛋。然而,彩蛋有一个魅力值,这个魅力值会随着彩蛋在空中降落的时间而降低,Sue要想得到更多的分数,必须尽量在魅力值高的时候收集这个彩蛋,而如果一个彩蛋掉入海中,它的魅力值将会变成一个负数,但这并不影响Sue的兴趣,因为每一个彩蛋都是不同的,Sue希望收集到所有的彩蛋。 然而Sandy就没有Sue那么浪漫了,Sandy希望得到尽可能多的分数,为了解决这个问题,他先将这个游戏抽象成了如下模型: 以Sue的初始位置所在水平面作为x轴。 一开始空中有N个彩蛋,对于第i个彩蛋,他的初始位置用整数坐标(xi, yi)表示,游戏开始后,它匀速沿y轴负方向下落,速度为vi单位距离/单位时间。Sue的初始位置为(x0, 0),Sue可以沿x轴的正方向或负方向移动

洛谷 P1854 花店橱窗布置 题解

[亡魂溺海] 提交于 2019-12-03 17:28:02
洛谷 P1854 花店橱窗布置 题解 Analysis 给定一个f*v的矩阵 要求从第一行走到第f行,每行取走一个数, 且该行所取的数必须必上一行所取的数的列数大 , 求所能取走的最大值 注意每一行所取走的数字的列数必须大于等该行的行号 因为必须给前面的花留下足够的花瓶 同理每一行所能取的最大的花瓶号必须小于等于v-(f-该行行数) 由此我们便可以很容易的得出状态转移方程 dp[i][j]=max(dp[i-1][k])+d[i][j](k<j) d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ k ] ) + d [ i ] [ j ] ( k < j ) 其中 dp[i][j] d p [ i ] [ j ]表示从第一行走到第i行并取走该行第j个数所能取得的最大值 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stack> 6 #define int long long 7 #define maxn 100+10 8 #define INF 9223372036854775807 9 using namespace std; 10 inline int read() 11 { 12 int x=0; 13

Codeforces1256E_Yet Another Division Into Teams

萝らか妹 提交于 2019-12-03 15:31:19
题意 n个人,每人有一个能力值a[i],要求分成多个队伍,每个队伍至少3个人,使得所有队伍的max(a[i])-min(a[i])之和最小。 分析 不会巧妙的dp,想了一天只想到了暴力的dp。 先排序,设 \(dp[i]\) 表示到前i个数组队,所有队伍的最小极差和。 转移方程为 \(dp[i]=min(dp[j-1]+a[i]-a[j])\) for j in 1...i-2。即 \(dp[i]=a[i]+min(dp[j-1]-a[j])\) 。 所以可以枚举i,然后用优先队列维护 \(dp[j-1]-a[j]\) ,注意j最大是i-2。 为了方便最后输出方案,再维护一个len数组,表示前i个人当前i所在队伍的人数,最后从后往前递推,每次 \(i-=len[i]-1\) ,就能标记队伍的分割点,然后类似差分的思想扫一遍即可得到答案。 还有10天就退役了,退役前不会dp,希望退役后能学会dp吧。 代码 #include <bits/stdc++.h> using namespace std; const int N=2e5+50; typedef long long ll; int n; pair<ll,int> a[N]; ll dp[N]; int ans[N],vis[N],len[N]; queue<int> tmp; struct node{ ll dp; int i;

P1896 [SCOI2005]互不侵犯

和自甴很熟 提交于 2019-12-03 14:11:37
原题链接 https://www.luogu.org/problem/P1896 闲话时刻: 日常打开洛谷,看了一眼智能推荐题目: 1896 [SCOI2005]互不侵犯? 水题啊! 嗯,一道状压 dp 好题,之前老早就想做了,正好练练状压 dp,于是就开始做了起来; 题目大意: 一般来说题目越短越难。 在一个 n * n 的图中选 k 个点,使得 以每个点为中心的九宫格内有且只有它一个点 ,问方案数; 题解: 一道很好的状压 dp 练手题; 状压状压,就是把一种状态压缩成一个数,达到节省空间的目的; 举个栗子: 假设我们有一行是这样放的国王(红色位置表示放国王): 正常操作:我们可以开一个一维数组 f [ i ] 表示第 i 个位置有没有放上国王; 状压操作: 上面的渣渣真是费空间,看我的! 由于每个格子只有两种状态: 放国王 和 不放国王 ,所以我们可以用 1 表示放国王,0 表示不放国王; 那么把每个格子的数连起来就是: 这是什么?如果我们将这个01串看成是一个二进制数的话,那么这种状态就被我们完美的表示成了一个数,这个数在十进制下是:(101001) 2 = ( 41) 10 所以状压 dp 的套路就是不断的去枚举表示状态的数,去转移即可。 状态设置: 按照状压 dp 的套路,我们设状态: dp [ i ][ j ][ S ] 表示我们已经选到了第 i 行,第 i