DP刷题记录和总结
来源:互联网 发布:id设计软件 编辑:程序博客网 时间:2024/05/19 03:41
今天开始刷dp专题,主要是做黄学长的blog上的题目和poj、bzoj上的dp题目,联赛还有两个月左右,fight!!!不苦不累,你要青春干嘛?!!
树形dp网址
有依赖型的树形背包
vijos1642
传送门
这道题是一道典型的有依赖型背包,必须要选了父亲才能选儿子,做这道题有两种方法,一种是
dfs序的方法就是考虑dfs序的性质,一个节点的编号到这个节点的编号加上它的子树大小-1就是它的子树的dfs序的区间范围。有了这个性质,我们考虑设dp状态为,
/************************************************************************* > File Name: vijos1642.cpp > Author: Drinkwater-cnyali > Created Time: 2017/9/3 23:20:01************************************************************************/#include<iostream>#include<cstdio>#include<cstdlib>#include<cmath>#include<cstring>#include<algorithm>using namespace std;#define REP(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)#define DREP(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i)#define mem(a, b) memset((a), b, sizeof(a))int read(){ int sum = 0, fg = 1; char c = getchar(); while(c < '0' || c > '9') { if (c == '-') fg = -1; c = getchar(); } while(c >= '0' && c <= '9') { sum = sum * 10 + c - '0'; c = getchar(); } return sum * fg;}const int maxn = 10000+10;int n,m;int be[maxn],ne[maxn],to[maxn],e;void add(int x,int y){ to[++e] = y;ne[e] = be[x];be[x] = e;}int dp[1010][1010];int tid[maxn],size[maxn],cnt,v[maxn];void dfs(int x,int fa){ tid[++cnt] = x;size[x] = 1; for(int i = be[x]; i; i = ne[i]) { int v = to[i]; if(v != fa)dfs(v,x),size[x] += size[v]; }}int main(){ n = read(),m = read(); REP(i,1,n) { v[i] = read(); int num = read(); REP(j,1,num) { int x = read(); add(i,x); } } dfs(1,0); mem(dp,-127); REP(i,1,n)dp[i][0] = 0,dp[i][1] = v[tid[i]]; DREP(i,n-1,1) REP(j,1,m) dp[i][j] = max(dp[i+1][j-1]+v[tid[i]],dp[i+size[tid[i]]][j]); int ans = -0x3f3f3f3f; REP(i,1,m)ans=max(ans,dp[1][i]); cout<<max(ans,0)<<endl; return 0;}
HDU2196
传送门
这是一道非常经典的树形dp的题目,问的是每个点到离他最远的的点的距离.
感觉这题稍微变一下就是树的直径了.
在纸上画一下我们发现,如果我们事先用dp处理好当前这个点到他子树内部最远的点的距离,离他最远的点要么在他的子树里,要么一定跟他父亲相连.所以我们预先处理好最大值与次大值,次大值的作用是如果他父亲的最远点同时也在当前点的子树里,我们就不能用父亲的最大值来更新,必须要用次大值,这样的话,我们再dfs一遍,就好了.
void bfs(int x,int fa){ for(int i = be[x]; i;i = ne[i]) { int v = to[i]; if(v != fa) { bfs(v,x);//更新最大值与次大值 if(dp[v][0] + w[i] >= dp[x][0])//最大值可以被更新 { dp[x][1] = dp[x][0];num[x][1] = num[x][0];//更新次大值 dp[x][0] = dp[v][0] + w[i];num[x][0] = v; }//次大值可以被更新 else if(dp[v][0] + w[i] > dp[x][1])dp[x][1] = dp[v][0] + w[i],num[x][1] = v; } }}
void dfs(int x,int fa){ for(int i = be[x]; i; i = ne[i]) { int v = to[i]; if(v != fa) { if(num[x][0] == v)//在同一颗子树里 { if(dp[v][1] < dp[x][1] + w[i])//次大值可以被更新,这里更新次大值的主要目的是用它来跟最大值比较 { dp[v][1] = dp[x][1] + w[i]; num[v][1] = x; } } else { if(dp[v][1] < dp[x][0] + w[i]) { dp[v][1] = dp[x][0] + w[i]; num[v][1] = x; } } if(dp[v][0] < dp[v][1]) { swap(dp[v][0],dp[v][1]); swap(num[v][0],num[v][1]); } dfs(v,x); } }}
URAL 1018
传送门
这道题也是十分经典,求的是树中每个点有权值,问只留下k个点剩下的最大权值和是多少?留下的点还是构成一棵树.
很显然,我们只需要处理每一颗子树就好了,所以状态很好设
转移也特别简单,我们观察到,设当前这个节点i的某个儿子为v.
void dfs(int x){ vis[x] = 1;size[x] = 1; for(int i = be[x]; i; i = ne[i]) { int v = to[i]; if(!vis[v]) { dfs(v); size[x] += size[v]; for(int j = size[x]; j >= 1; --j) REP(k,1,j-1) dp[x][j] = max(dp[x][j],dp[x][j-k] + dp[v][k] + w[i]); } }}
poj3162
传送门
跟上上道题一样,但是加了一问,把距离处理出来之后,他问你最长的满足区间最大值-最小值<=m的区间长度。提供两个思路,第一个是指针加线段树,我们枚举区间的左右端点,不满足条件左端点++,不然更新答案,右端点++,这个也可以用单调队列来搞,应该很快,这道题我还稍微卡了卡常。另外的就是二分一个长度,然后nlogn的判断就好了。
/************************************************************************* > Author: Drinkwater > Created Time: 2017/9/6 18:11:55 ************************************************************************/#include<iostream>#include<cstdio>#include<cstdlib>#include<cmath>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;typedef unsigned long long uLL;#define REP(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)#define DREP(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i)#define mem(a, b) memset((a), b, sizeof(a))template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }int read(){ register int sum = 0,fg = 0;char c = getchar(); while(c < '0' || c > '9') { fg |= c == '-'; c = getchar(); } while(c >= '0' && c <= '9') { sum = sum * 10 + c - '0'; c = getchar(); } return fg ? -sum : sum;}const int inf = 1e9;const int maxn = 1000000+10;int n,m;int be[maxn<<1],ne[maxn<<1],to[maxn<<1],e,w[maxn<<1];int dp[maxn][2],num[maxn][2];void add(int x,int y,int z){ to[++e] = y;ne[e] = be[x];be[x] = e;w[e] = z; to[++e] = x;ne[e] = be[y];be[y] = e;w[e] = z;}void bfs(int x,int fa){ dp[x][0] = dp[x][1] = 0; for(int i = be[x]; i;i = ne[i]) { int v = to[i]; if(v != fa) { bfs(v,x); if(dp[v][0] + w[i] >= dp[x][0]) { dp[x][1] = dp[x][0];num[x][1] = num[x][0]; dp[x][0] = dp[v][0] + w[i];num[x][0] = v; } else if(dp[v][0] + w[i] > dp[x][1])dp[x][1] = dp[v][0] + w[i],num[x][1] = v; } }}void dfs(int x,int fa){ for(int i = be[x]; i;i = ne[i]) { int v = to[i]; if(v != fa) { if(num[x][0] == v) { if(dp[x][1] + w[i] > dp[v][1]) dp[v][1] = dp[x][1] + w[i], num[v][1] = x; } else { if(dp[x][0] + w[i] > dp[v][1]) dp[v][1] = dp[x][0] + w[i],num[v][1] = x;} if(dp[v][1] > dp[v][0])swap(dp[v][1],dp[v][0]),swap(num[v][0],num[v][1]); dfs(v,x); } }}int Max[maxn<<2],Min[maxn<<2];void build(int h,int l,int r){ if(l == r){Max[h] = Min[h] = dp[l][0];return ;} int mid = (l + r) >> 1; build(h<<1,l,mid);build(h<<1|1,mid+1,r); Max[h] = max(Max[h<<1],Max[h<<1|1]); Min[h] = min(Min[h<<1],Min[h<<1|1]);}int queryB(int h,int l,int r,int q,int w){ if(l >= q && w >= r)return Max[h]; int mid = (l + r) >> 1; int Ans = -inf; if(q <= mid)chkmax(Ans,queryB(h<<1,l,mid,q,w)); if(w > mid)chkmax(Ans,queryB(h<<1|1,mid+1,r,q,w)); return Ans;}int queryS(int h,int l,int r,int q,int w){ if(l >= q && w >= r)return Min[h]; int mid = (l + r) >> 1; int Ans = inf; if(q <= mid)chkmin(Ans,queryS(h<<1,l,mid,q,w)); if(w > mid)chkmin(Ans,queryS(h<<1|1,mid+1,r,q,w)); return Ans;}int main(){ while(scanf("%d%d",&n,&m)!=EOF) { mem(be,0);e = 0; REP(i,2,n) { int x = read(),y = read(); add(i,x,y); } bfs(1,0);dfs(1,0); build(1,1,n); int l = 1,r = 1; int ans = 0; while(1) { if(l>r||l>n||r>n) break; int Mx = queryB(1,1,n,l,r); int Mn = queryS(1,1,n,l,r); if(Mx - Mn <= m)chkmax(ans,r-l+1),++r; else ++l; } printf("%d\n",ans); } return 0;}
P3195
传送门
这题的
观察这个式子,发现后面的式子跟i,j都有关,不能用线段树或单调队列,于是我们想到了斜率优化。斜率优化就是证明一个决策单调性,然后把式子化成斜率式,每次用单调队列维护下凸包就好了。
对于这道题的话,我们设
我们假设
即:
设i的后继状态为t,
即证:
等价于
展开得
设
原式等于
所以
于是我们就证明了决策单调性.
于是我们把原式化为斜率形式
然后就好了
LL cl(LL x) { return x * x;}double slope(int x,int y){ return (dp[x] - dp[y] + cl(f[x] + L) - cl(f[y] + L))/(2.0 * (f[x]-f[y]));}int main(){ n = read(),L = read();L++; REP(i,1,n)w[i] = read(),sum[i] = sum[i-1] + w[i]; REP(i,1,n)f[i] = sum[i] + i; dp[1] = 0; int l = 1,r = 1; REP(i,1,n) { while(l < r && slope(q[l],q[l+1]) <= f[i])l++; dp[i] = dp[q[l]] + cl(f[i]-f[q[l]]-L); while(l < r && slope(q[r-1],q[r]) > slope(q[r],i))r--; q[++r] = i; } cout<<dp[n]<<endl;}
HDU1011
传送门
树形背包,每个节点可以当成一个决策,跟第一道题一样,这里用树形背包的放法写一下。
void dfs(int x){ vis[x] = 1; int dg = (c[x] + 19) / 20; REP(i,dg,m)dp[x][i] = p[x]; for(int i = be[x]; i; i = ne[i]) { int v = to[i]; if(!vis[v]) { dfs(v); DREP(j,m,dg) for(int k = 1;j + k <= m; ++k) dp[x][j+k] = max(dp[x][j+k],dp[x][j]+dp[v][k]); } }}
POJ3107
传送门
求树的重心,就是把这个点去掉后,最大的连通块最小。
设
void dfs(int x,int fa){ size[x] = 1; for(int i = be[x]; i; i = ne[i]) { int v = to[i]; if(v != fa) { dfs(v,x);size[x] += size[v]; dp[x] = max(dp[x],size[v]); } } dp[x] = max(dp[x],n-size[x]); if(dp[x] < dp[root])root = x;}
codeforces 219D
传送门
给你一棵树,树边有方向,正向边权值为0,反向为1,问以那些点为根,使之遍历(从这个点往下走)所有树边的权值和最小。
一开始我们设
void bfs(int x,int fa){ for(int i = be[x]; i; i = ne[i]) { int v = to[i]; if(v != fa) { bfs(v,x); dp[x] += dp[v] + w[i]; } }}void dfs(int x,int fa){ for(int i = be[x]; i; i = ne[i]) { int v = to[i]; if(v != fa) { dp[v] += (dp[x] - dp[v] - w[i]) + (w[i]^1); dfs(v,x); } }}
POJ1947
传送门
问你最少割去几条边使得有大小为k的联通块.
实际上还是挺简单的,做到现在发现树型dp就是在递归的时候调用子树信息,实际上其他dp也是这样,利用后继节点的信息,这道题我们设
void bfs(int x,int fa){ size[x] = 1; for(int i = be[x]; i;i = ne[i]) { int v = to[i]; if(v != fa) { bfs(v,x);son[x]++; size[x] += size[v]; } } dp[x][1] = son[x];}void dfs(int x,int fa){ for(int i = be[x]; i; i= ne[i]) { int v = to[i]; if(v != fa) { dfs(v,x); DREP(j,m,1) REP(k,1,size[v]) if(j-k>=1)dp[x][j] = min(dp[x][j],dp[x][j-k]+dp[v][k]-1); } }}
int ans = dp[1][m];REP(i,2,n)ans = min(ans,dp[i][m]+1);//这里要注意,因为是强制选,所以跟他父亲的那个点要断开.
HDU1561
传送门
是一道树型背包的好题,算是入门的吧,这里解释下为什么背包合并的时候为什么要逆序枚举,其实也好理解,我们不是一个一个子节点枚举的吗?这样我们的dp方程
void dfs(int x){ size[x] = 1; for(int i = be[x]; i; i = ne[i]) { int v = to[i]; dfs(v);size[x] += size[v]; DREP(j,m,1) REP(k,1,size[v]) if(j - k >= 1)dp[x][j] = max(dp[x][j],dp[x][j-k]+dp[v][k]); }}
HDU4003
传送门
有了上面那题的思想,这道题就好理解了,好像网上说的东西我不是很懂,那个分组背包有点没懂,但是感觉我的理解也没错.树中每条边有花费,有k个机器人,问遍历所有的点的最少花费(可以回头,每次只能从s出发)
我们先证明个东西,就是对于一个机器人a,他是不可能从别的子树过来又回去的,因为我们设在这个子树有一个机器人b,他可以走完自己的路径然后走a的路径,这样会少走a会a所在的子树的那条路径.
所以,对于一个子树内机器人,他就一定是走完这棵子树,不会走到别的地方去,所以我们设
#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<cmath>#include<algorithm>using namespace std;#define REP(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)#define DREP(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i)#define mem(a, b) memset((a), b, sizeof(a))typedef long long LL;int read(){ register int fg = 1,sum = 0;char c = getchar(); while(!isdigit(c)) { if(c == '-')fg = -1; c = getchar(); } while(isdigit(c)) { sum = sum * 10 + c - '0'; c = getchar(); } return fg * sum;}const int maxn = 20000+10;int n,s,k;int be[maxn],ne[maxn],to[maxn],e,w[maxn];int dp[maxn][11];void add(int x,int y,int z){ to[++e] = y;ne[e] = be[x];be[x] = e;w[e] = z;}void dfs(int x,int fa){ for(int i = be[x]; i; i = ne[i]) { int v = to[i]; if(v != fa) { dfs(v,x); DREP(j,k,0) { dp[x][j] += dp[v][0] + 2 * w[i]; REP(l,1,j)dp[x][j] = min(dp[x][j],dp[x][j-l]+dp[v][l]+l*w[i]); } } }}int main(){#ifndef ONLINE_JUDGE freopen("1.in","r",stdin); freopen("1.out","w",stdout);#endif while(scanf("%d%d%d",&n,&s,&k)!=EOF) { mem(be,0);e = 0;mem(dp,0); REP(i,1,n-1) { int x = read(),y = read(),z = read(); add(x,y,z);add(y,x,z); } dfs(s,-1); cout<<dp[s][k]<<endl; }}
USACO2.3奶牛家谱
传送门
这道题拖了我好久,一直没做,今晚上推出来一个n^4的dp,以为做不了,但是卡卡常数就过了。。。
我们设
#include<iostream>#include<cstdio>#include<cstdlib>#include<cmath>#include<cstring>#include<algorithm>using namespace std;#define REP(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)#define DREP(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i)#define mem(a, b) memset((a), b, sizeof(a))int read(){ int sum = 0, fg = 1; char c = getchar(); while(c < '0' || c > '9') { if (c == '-') fg = -1; c = getchar(); } while(c >= '0' && c <= '9') { sum = sum * 10 + c - '0'; c = getchar(); } return sum * fg;}const int mod = 9901;int n,m;int dp[110][210][210];int c[220][220];int main(){ n = read(),m = read(); REP(i,1,210)c[i][i] = 1,c[i][1] = i,c[i][0] = 1; REP(i,2,210) REP(j,1,i)c[i][j] = (c[i-1][j] + c[i-1][j-1])%mod; dp[1][1][1] = 1;dp[2][2][3] = 1; REP(i,3,m) for(int j = 2;j <= n/2+1; j += 2) { if(i < 31)if(1<<i < j)continue; for(int k = j+1;k <= n; k ++) for(int l = j/2; l <= n/2+1; l++) if(l%2==0)(dp[i][j][k] += dp[i-1][l][k-j]*c[l][j/2])%=mod; } int ans = 0; REP(i,2,n)(ans += dp[m][i][n])%=mod,++i; cout<<ans<<endl; return 0;}
NOIP2015子串
传送门
这道题我也是拖了好久没做,今晚上随便推了推,发现有70pt,开始设定的状态是
REP(i,1,n)if(s[i] == p[1])dp[i][1][1] = 1; REP(k,1,f) REP(i,1,n) { REP(j,1,m) { if(s[i] != p[j])continue; REP(l,1,i-1) { if(s[l] != p[j-1])continue; if(l==i-1)(dp[i][j][k] += dp[l][j-1][k])%=mod; (dp[i][j][k] += dp[l][j-1][k-1])%=mod; } } }
后来发现这东西不好优化,于是推了另一种状态
定义状态
这样子转移就很好写了
/************************************************************************* > File Name: zj.cpp > Author: Drinkwater-cnyali > Created Time: 2017/9/9 20:45:29************************************************************************/#include<iostream>#include<cstdio>#include<cstdlib>#include<cmath>#include<cstring>#include<algorithm>using namespace std;#define REP(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)#define DREP(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i)#define mem(a, b) memset((a), b, sizeof(a))int read(){ int sum = 0, fg = 1; char c = getchar(); while(c < '0' || c > '9') { if (c == '-') fg = -1; c = getchar(); } while(c >= '0' && c <= '9') { sum = sum * 10 + c - '0'; c = getchar(); } return sum * fg;}const int mod = 1e9 + 7;const int maxn = 210;int dp[2][maxn][maxn][2],n,m,f,sum;char s[1010],p[1010];int main(){ n = read(),m = read(),f = read(); cin>>(s+1)>>(p+1); REP(i,1,n) { dp[i&1][1][1][0] = sum; if(s[i] == p[1])dp[i&1][1][1][1] = 1,sum++; REP(j,2,m) { REP(k,1,f) { dp[i&1][j][k][0] = (dp[(i+1)&1][j][k][0]+dp[(i+1)&1][j][k][1])%mod; if(s[i] == p[j])dp[i%2][j][k][1]=((dp[(i+1)%2][j-1][k-1][1]+dp[(i+1)%2][j-1][k][1])%mod+dp[(i+1)%2][j-1][k-1][0])%mod; } } REP(j,1,m) REP(k,1,f) dp[(i+1)%2][j][k][1]=dp[(i+1)%2][j][k][0]=0; } cout<<(dp[n&1][m][f][0]+dp[n&1][m][f][1])%mod<<endl; return 0;}
HDU3586
传送门
这题就是说,给一个限制m,切断的路径权值和不超过m,单个边权值也不超过k,求最小的k使得所有叶子和根不相连。其实这题蛮简单的,二分一个k,我们设
void dfs(int x,int lim,int fa){ dp[x] = 0;int flag = 0; for(int i = be[x]; i; i = ne[i]) { int v = to[i]; if(v != fa) { flag = 1; dfs(v,lim,x); int ls = w[i] <= lim ? w[i] : inf; dp[x] += min(dp[v],ls); } } if(!flag)dp[x] = inf;}
HDU4276
传送门
巧妙的转化一下思路,把最短路的边权赋成0,然后跑一遍树形背包就好了
/************************************************************************* > Author: Drinkwater > Created Time: 2017/9/10 20:24:38 ************************************************************************/#include<iostream>#include<cstdio>#include<cstdlib>#include<cmath>#include<cstring>#include<algorithm>#include<queue>#prag\ma GCC optimize("O3")using namespace std;typedef long long LL;typedef unsigned long long uLL;#define REP(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)#define DREP(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i)#define mem(a, b) memset((a), b, sizeof(a))template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }int read(){ register int sum = 0,fg = 0;char c = getchar(); while(c < '0' || c > '9') { fg |= c == '-'; c = getchar(); } while(c >= '0' && c <= '9') { sum = sum * 10 + c - '0'; c = getchar(); } return fg ? -sum : sum;}const int inf = 1e9;const int maxn = 110;int n,m;int be[maxn<<1],ne[maxn<<1],to[maxn<<1],w[maxn<<1],e;void add(int x,int y,int z){ to[e] = y;ne[e] = be[x];be[x] = e;w[e] = z;e++;}int vis[maxn],pre[maxn],id[maxn],dis[maxn],dp[maxn][maxn*5];bool bfs(){ queue<int>q;mem(vis,0); fill(dis+1,dis+1+n,inf); vis[1] = 1;dis[1] = 0;q.push(1);pre[1] = 1; while(!q.empty()) { int x = q.front();q.pop(); if(x == n)break; for(int i = be[x]; i!=-1; i = ne[i]) { int v = to[i]; if(!vis[v]) { if(chkmin(dis[v],dis[x]+w[i])) q.push(v),vis[v] = 1,pre[v] = x,id[v] = i; } } } for(int x = n;x != 1; x = pre[x]) w[id[x]] = w[id[x]^1] = 0; if(dis[n] > m)return 0; return 1;}int val[maxn];void dfs(int x,int fa){ REP(i,0,m)dp[x][i] = val[x]; for(int i = be[x]; i!=-1; i = ne[i]) { int v = to[i]; if(v != fa) { dfs(v,x); DREP(j,m,0) REP(k,0,j) if(j-k-2*w[i]>=0) dp[x][j] = max(dp[x][j],dp[v][k]+dp[x][j-k-2*w[i]]); } }}int main(){ while(scanf("%d%d",&n,&m)!=EOF) { mem(be,-1);e = 0; REP(i,1,n-1) { int x = read(),y = read(),z = read(); add(x,y,z);add(y,x,z); } REP(i,1,n)val[i] = read(); int tmp = bfs(); if(!tmp)puts("Human beings die in pursuit of wealth, and birds die in pursuit of food!"); else { mem(dp,0);dfs(1,-1);m -= dis[n]; printf("%d\n",dp[1][m]); } } return 0;}
HAOI 逆序对数列
传送门
首先n^3的dp很好想,于是我们可以用前缀和来优化dp就好了。
/************************************************************************* > File Name: bzoj2431.cpp > Author: Drinkwater-cnyali > Created Time: 2017/9/12 23:49:05************************************************************************/#include<iostream>#include<cstdio>#include<cstdlib>#include<cmath>#include<cstring>#include<algorithm>using namespace std;#define REP(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)#define DREP(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i)#define mem(a, b) memset((a), b, sizeof(a))int read(){ int sum = 0, fg = 1; char c = getchar(); while(c < '0' || c > '9') { if (c == '-') fg = -1; c = getchar(); } while(c >= '0' && c <= '9') { sum = sum * 10 + c - '0'; c = getchar(); } return sum * fg;}const int maxn = 1010;const int mod = 10000;int dp[maxn][maxn];int n,k,sum[maxn];int main(){ n = read(),k = read(); dp[1][0] = 1;sum[1] = 1; REP(i,1,k+1)sum[i] = 1; REP(i,2,n) { REP(j,0,min(k,(i-1)*i/2)) (dp[i][j] += ((sum[j+1] - sum[max(0,j-i+1)])+mod)%mod)%=mod; sum[0] = 0; REP(j,0,k)(sum[j+1] = sum[j] + dp[i][j])%=mod; } cout<<dp[n][k]<<endl; return 0;}
BZOJ2466
传送门
很好理解的,
显然的是,
于是我们可以推出一下dp,s为i的儿子
记住是子树全亮,所以我们后记状态都是[..][..][1]
void dfs(int x,int fa){ int t1 = 0,t2 = inf,t3 = 0; //t1 : dp[x][0][0] //t2 : dp[x][0][1] //t3 : dp[x][1][1] for(int i = be[x]; i; i = ne[i]) { int v = to[i]; if(v != fa) { dfs(v,x); int s1 = t1,s2 = t2; t1 = min(dp[v][0][1] + s1,dp[v][1][1] + s2); t2 = min(dp[v][1][1] + s1,dp[v][0][1] + s2); t3 += dp[v][0][0]; } } dp[x][0][0] = t1;dp[x][0][1] = t2;dp[x][1][1] = t3 + 1;}
BZOJ1231
传送门
一道很简单的状压dp,设状态
/************************************************************************* > File Name: bzoj1231.cpp > Author: Drinkwater-cnyali > Created Time: 2017/9/14 23:52:24************************************************************************/#include<iostream>#include<cstdio>#include<cstdlib>#include<cmath>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;#define REP(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)#define DREP(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i)#define mem(a, b) memset((a), b, sizeof(a))int read(){ int sum = 0, fg = 1; char c = getchar(); while(c < '0' || c > '9') { if (c == '-') fg = -1; c = getchar(); } while(c >= '0' && c <= '9') { sum = sum * 10 + c - '0'; c = getchar(); } return sum * fg;}const int maxn = 16;int n,k;int s[maxn+10];LL dp[70000][17];int main(){ n = read(),k = read(); REP(i,1,n)s[i] = read(),dp[1<<(i-1)][i] = 1; REP(st,0,(1<<n)-1) { REP(i,1,n) { if(st & (1<<(i-1)))continue; REP(j,1,n) { if(st & (1<<(j-1)) && abs(s[j]-s[i]) > k) dp[st ^ (1<<(i-1))][i] += dp[st][j]; } } } LL ans = 0; REP(i,1,n)ans += dp[(1<<n)-1][i]; cout<<ans<<endl; return 0;}
- DP刷题记录和总结
- 【GRE】做题记录和总结
- OI刷题记录
- OI刷题记录~
- leetcode刷题记录
- 刷题记录
- 6.22刷题记录
- 7.26-刷题记录
- hdu 刷题记录
- 面试刷题记录
- 每日刷题记录
- leetcode刷题记录
- hdu刷题记录
- LightOJ刷题记录
- leetcode刷题记录
- 刷题记录
- leetcode 刷题记录
- 刷题记录
- 波形包络提取与峰值提取
- 解法汇总:找数组中前K大(小)个数
- Android拾萃- Activity的生命周期和启动模式
- TCP三次握手过程
- 日期常用工具类
- DP刷题记录和总结
- Atitit 企业常见100个职能 组织职能 社会职能 政府职能 家庭职能 团队职能
- 2017/9/3
- Atitit 企业6大职能 attilax总结
- Block Site
- atitit 组织机构性质与名称表.docx
- 四、ElasticSearch5.5.2安装使用Kibana监控及配置
- 《Effective C++》1-让自己习惯C++
- Linux驱动的等待队列、轮询及内核线程