dp练习orz
来源:互联网 发布:c语言接口与实现 编辑:程序博客网 时间:2024/04/29 10:21
1.51nod1296
对于一段,限制只有前一个和它本身。
定义
转移也很显然。
#include <bits/stdc++.h>#define Rep(i,n) for(int i = 1;i <= n;++ i)using namespace std;const int Mod = 1e9 + 7;int n,K,L,sum[5002],f[2][5002],pos[5005];int main(){ scanf("%d%d%d",&n,&K,&L); Rep(i,K){int x;scanf("%d",&x);++ x;pos[x] = 1;} Rep(i,L){int x;scanf("%d",&x);++ x;pos[x] = 2;} sum[1] = 1;sum[0] = 0; for(int i = 2;i <= n;++ i) { int cur = i & 1; Rep(j,i) { if(!pos[i]) { if(!pos[i - 1])f[cur][j] = sum[i - 1]; else if(pos[i - 1] == 1)f[cur][j] = sum[j - 1]; else f[cur][j] = (sum[i - 1] - sum[j - 1] + Mod) % Mod; } else if(pos[i] == 1) { if(!pos[i - 1])f[cur][j] = (sum[i - 1] - sum[j - 1] + Mod) % Mod; else if(pos[i] == 1)f[cur][j] = 0; else f[cur][j] = (sum[i - 1] - sum[j - 1] + Mod) % Mod; } else { if(!pos[i - 1])f[cur][j] = sum[j - 1]; else if(pos[i - 1] == 1)f[cur][j] = sum[j - 1]; else f[cur][j] = 0; } } Rep(j,i)sum[j] = (sum[j - 1] + f[cur][j]) % Mod; } printf("%d\n",sum[n]); return 0;}
关键在于想到排名OTZ
2.51nod1043
水题一道,实际上我们对第一位强制让它选一个数就好了。
#include <bits/stdc++.h>#define Rep(i,n) for(int i = 1;i <= n;++ i)using namespace std;const int N = 9005;const int Mod = 1e9 + 7;int f[N],n,g[N];int main(){ f[0] = 1; g[0] = 1; scanf("%d",&n); Rep(i,n) for(int j = 9 * n;j;-- j) for(int k = 1;k <= 9;++ k) if(j >= k) { f[j] = (f[j] + f[j - k]); if(f[j] >= Mod) f[j] %= Mod; } for(int i = 2;i <= n;++ i) for(int j = 9 * n;j;-- j) for(int k = 1;k <= 9;++ k) if(j >= k) { g[j] += g[j - k]; if(g[j] >= Mod)g[j] %= Mod; } int sum = 0; for(int i = 1;i <= 9 * n;++ i)sum = (sum + 1ll * (f[i] - g[i] + Mod) * f[i] % Mod) % Mod; printf("%d\n",sum); return 0;}
真的得点一点DP技能树。
3.bzoj4321
考虑到这个东西肯定是第i个沙茶去更新原来的队伍。
肯定的一点是,我们的不合法状态数目会因为第i个人发生改变。
定义状态
则:
k = 1:挨着 => 前一个人假如和i-2这个人挨着的话,那么在不拆散的情况下,就只有一个位置。如果不挨着的话就是两个位置。
考虑拆散的话,不合法数目不变。
k = 0:麻烦一点,考虑到实际上这个人有可能自己安静的呆着,也有可能去烧烧烧。
假设他安静地呆着的话,那么我们知道一共有i个空位置,但是有j+2个位置他不能去拆散。
但是这样在前两个人挨着的时候实际上只有j+1个位置不能去选。
如果他去拆别人:
有j+1个位置可以拆。
但是如果说前两个人挨着就只有j个位置可以拆了QAQ
#include<bits/stdc++.h>#define Rep(i,n) for(int i = 1;i <= n;i ++)using namespace std;int m,n;int f[1002][1002][2];typedef long long LL;const int Mod = 7777777;void add(int &x,LL y){if(y >= Mod)y %= Mod;x += y;if(x >= Mod)x -= Mod;}int main (){ f[1][0][0] = 1; scanf("%d",&n); for(int i = 2;i <= n;++ i) { for(int j = 0;j < n - 1;++ j) { int &st = f[i][j][1],&t = f[i][j][0]; st = f[i - 1][j][1]; if(j)add(st,f[i - 1][j - 1][0] * 2ll + f[i - 1][j - 1][1]); add(t,j * (LL)f[i - 1][j + 1][1]);//拆别人 add(t,(j + 1) * (LL)f[i - 1][j + 1][0]);//拆别人 add(t,(i - j - 1) * (LL)f[i - 1][j][1]); add(t,(i - j - 2) * (LL)f[i - 1][j][0]); } } printf("%d\n",f[n][0][0]); return 0;}
感谢何广荣大爷的题解orz
4.51nod1412:AVL树的种类
根据问题设计状态,f[i]表示有i个节点的AVL。
显然要枚举形态,再枚举个高度。
#include <bits/stdc++.h>#define Rep(i,n) for(int i = 1;i <= n;++ i)using namespace std;typedef long long LL;const int N = 2002;LL f[N][22];const int Mod = 1e9 + 7;int main(){ int n; scanf( "%d",&n); f[0][0] = f[1][1] = 1; Rep(i,n) { for(int j = 0;j < i;++ j) { for(int k = 2;k <= 16;++ k) { (f[i][k] += f[i - j - 1][k - 1] * f[j][k - 1]) %= Mod; if(k >= 2)(f[i][k] += 2ll * f[i - j - 1][k - 2] * f[j][k - 1]) %= Mod; } } } LL ans = 0; Rep(i,15)(ans += f[n][i]) %= Mod; printf("%lld\n",ans); return 0;}
5.51nod1354 选数字
肯定约数个数很少。
直接dp。
#include <bits/stdc++.h>#define Rep(i,n) for(int i = 1;i <= n;++ i)using namespace std;int n,K,cur,f[10001],a[1001];const int Mod = 1e9 + 7;vector<int>vec;map<int,int>mp;void Div(){ cur = 0; vec.clear(); mp.clear(); int i; for(i = 1;1ll * i * i < K;++ i) if(K % i == 0)mp[i] = cur ++,vec.push_back(i); if(K % i == 0)mp[i] = cur ++,vec.push_back(i); for(;i;-- i) if(K % i == 0)mp[K / i] = cur ++,vec.push_back(K / i);}void solve(){ Div(); int s = sqrt(K); memset(f,0,sizeof(f)); f[0] = 1; for(int i = 1;i <= n;++ i) for(int j = cur - 1;~ j;-- j) if(vec[j] % a[i] == 0)f[j] = (f[j] + f[mp[vec[j] / a[i]]]) % Mod; printf("%d\n",f[cur - 1]);}int main(){ int T;scanf("%d",&T); while(T --) { scanf("%d%d",&n,&K); Rep(i,n)scanf("%d",&a[i]); solve(); } return 0;}
6.51nod1232
这个数位DP比较好玩啊。
首先我们注意到肯定转移的时候是记录余数的。
实际上记录每一位的余数肯定不如直接记录LCM优秀。
这样思路就显而易见了。
我们对Mod lcm{1,2..,10}建立剩余系,在数位dp的时候记录当前的选中的数字的lcm是多少,以及在剩余系下的值。
那么我们知道组成的lcm的数目是很小的,所以我们离散化一下就行了。这样我们就解决了这个题目。
#include <bits/stdc++.h>#define Rep(i,n) for(int i = 1;i <= n;++ i)using namespace std;typedef long long LL;int Id[2550],cnt,bit[125],cur[100];LL f[21][2550][100];const int Mod = 1e9 + 7;int idx(int x){return Id[x] ? Id[x] : (Id[x] = ++ cnt,cur[cnt] = x,cnt);}int gcd(int x,int y){return !y ? x : gcd(y,x % y);}int LCM(int x,int y){return x * y / gcd(x,y);}int Get(int x,int y){return !y ? x : LCM(x,y);}LL dfs(int len,int re,int se,bool flag){ if(!len)return re % cur[se] == 0; LL st = f[len][re][se]; if(!flag && ~st)return st; st = 0; int t = flag ? bit[len] : 9; for(int i = 0;i <= t;++ i) { int lcm = Get(cur[se],i); int id = idx(lcm); st += dfs(len - 1,(re * 10 % 2520 + i) % 2520,id,flag && i == t); } if(!flag)f[len][re][se] = st; return st;}void init(){memset(f,-1,sizeof(f));cnt = 0;}LL solve(LL x){ if(x <= 9 && x >= 0)return x + 1; int len = 0; while(x)bit[++ len] = x % 10,x /= 10; cur[0] = 1; return dfs(len,0,0,1);}void readin(){ int T; scanf("%d",&T); while(T -- ) { LL x,y; scanf("%lld%lld",&x,&y); printf("%lld\n",solve(y) - solve(x - 1)); }}void end(){}int main(){ init(); readin(); end(); return 0;}
7.51nod1486
这个题目我觉得自己还不是特别会。
首先,补集转换是显然的。
我们把n个石子的约束转化成补集,即:
随意走 - 至少踩一个石子+至少踩两个石子-。。。
之后就不会做了……
这个容斥太笨拙了。
我们考虑一个更加优美的容斥:
随意走 - 不合法的方案数。
我可以直接求并集我为啥非要容斥呢。
不合法的方案数,显然对于一个不合法的方案只有一种情况,就是踩到的第一块石头不是
1.首先踩了第1块的。
2.首先踩了第2块的。
3.。。。。
i - 1.首先踩了第i - 1块的。
这几组方案各不相同。
然后我们知道,不合法的方案实际上分成这样几组之后,剩下的怎么走都不合法,并且怎么走都不是相同方案。这样转移也显然了。
也就是这个玩意就是个补集转化。
好了讲完了。
- dp练习orz
- 【堆+区间DP】Orz细菌(orz)
- Orz
- orz
- ORZ
- ...Orz
- Orz
- Orz
- orz
- DP练习
- dp练习
- [51nod1299]监狱逃离 树形DP || 20w个点的网络流最小割ORZ
- dp专题练习
- 数位DP练习
- 【线性DP】基础练习
- 学校作业-Dp练习
- 15.2.27 DP练习
- DP练习☞ <美元>
- StackOverflow 这么大,它的架构是怎么样的?
- 学习总结3
- 在eclipse运行tomcat时选择Use tomcat installation时弹出Publishing the configuration的解决方法
- 如何水平居中一个元素。
- 通过jQuery Ajax使用FormData对象上传文件
- dp练习orz
- C++ STL 一般总结
- 8C
- Android重写getResources规避用户调整系统字体大小影响Android屏幕适配
- Android 用Animation-list实现逐帧动画
- Android Support Multidex原理分析
- 理解和正确使用Java中的断言(assert)
- Android 图片缓存 - Fresco
- JavaScript实现年、月、日三下拉框联动