赞
踩
参考网上众多大神整理得出以下模板,如有不对之处,欢迎讨论
void bag01(int cost,int weight) {
for(i = v; i >= cost; --i)
dp[i] = max(dp[i], dp[i-cost]+weight);
}
(2)完全背包:
void complete(int cost, int weight) {
for(i = cost ; i <= v; ++i)
dp[i] = max(dp[i], dp[i - cost] + weight);
}
(3) 多重背包:
void multiply(int cost, int weight, int amount) {
if(cost * amount >= v)
complete(cost, weight);
else{
k = 1;
while (k < amount){
bag01(k * cost, k * weight);
amount -= k;
k += k;
}
bag01(cost * amount, weight * amount);
}
}
void solve() {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (s1[i] == s2[j]) {
dp[i + 1][j + 1] = dp[i][j] + 1;
}else {
dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]);
} } }
}
int Arr[30010],List[30010]; int LIS(int *Arr,int N) //arr[]存放的是待求数组 { int Max = 0; //max为最大递增子序列的长度 for(int i = 1; i <= N; ++i) List[i] = 1; //lis[i] 存放i之前的最大递增子序列的长度,初始都为1 for(int i = 2; i <= N; ++i) for(int j = 1; j < i; ++j) //遍历i之前所有位置 if(Arr[i] >= Arr[j] && List[i]<List[j]+1) List[i] = List[j] + 1; //arr[i]>arr[j]为递增 //lis[i]<lis[j] + 1确保为当前最长递增序列 for(int i = 1; i <= N; ++i) if(Max < List[i]) Max = List[i]; return Max; }
(2)求最长递增子序列的长度O(NlogN)
int Stack[10010]; int LIS(int *Arr,int N) { int top = 0; Stack[top] = -1; for(int i = 1; i <= N; ++i) { if(Arr[i] > Stack[top]) Stack[++top] = Arr[i]; else { int low = 1; int high = top; while(low <= high) { int mid = (low + high)/2; if(Arr[i] > Stack[mid]) low = mid + 1; else high = mid - 1; } Stack[low] = Arr[i]; } } return top; }
int a[110000],N,pos1,pos2,Start,End; //Start、End存储最大连续子序列的起点和终点 int MaxSubSum(int *a) { int MaxSum = a[0],Sum = a[0]; pos1 = pos2 = Start = End = 0; for(int i = 1; i < N; ++i) { Sum += a[i]; if(Sum < a[i]) { Sum = a[i]; pos1 = i; pos2 = i; } else { pos2 = i; } if(MaxSum < Sum) { MaxSum = Sum; Start = pos1; End = pos2; } } return MaxSum; }
char s[2020]; int dp[2020][2020]; //dp[i][j]表示s[i~j]最长回文子序列 int LPS(char *s) { memset(dp,0,sizeof(dp)); int len = strlen(s); for(int i = len-1; i >= 0; --i) { dp[i][i] = 1; for(int j = i+1; j < len; ++j) { if(s[i] == s[j]) //头尾相同,最长回文子序列为去头尾的部分LPS加上头和尾 dp[i][j] = dp[i+1][j-1] + 2; else //头尾不同,最长回文子序列是去头部分的LPS和去尾部分LPS较长的 dp[i][j] = max(dp[i][j-1],dp[i+1][j]); } } return dp[0][len-1]; }
char str[2000020],s[2000020]; //str为待求的原串,s为处理后的新串 int P[2000020]; //P[i]记录的是以字符str[i]为中心的最长回文串的半径 void Pre(char *str) { int len = strlen(str); s[0] = '$'; s[1] = '#'; for(int i = 0; i < len; ++i) { s[i*2+2] = str[i]; s[i*2+3] = '#'; } s[len*2+2] = '\0'; } //返回最长回文子串长度 int Manacher(char *s) { int Max = 0; int len = strlen(s); int id = 0; for(int i = 1; i < len; ++i) { if(Max > i) P[i] = min(P[2*id-i],P[id]+id-i); else P[i] = 1; while(s[i+P[i]] == s[i-P[i]]) P[i]++; if(P[i]+i > Max) { Max = P[i]+i; id = i; } } int ans = 0; for(int i = 2; i < len; ++i) if(ans < P[i]) ans = P[i]; return ans-1;//返回最长回文子串长度 }
int dp[1010][1010],len1,len2; char s1[1010],s2[1010]; int EditDist(char *s1,char *s2) { int len1 = strlen(s1); int len2 = strlen(s2); //当两个字符串的大小为0,其操作距离为0。 //当其中一个字符串的长度是零,需要的操作距离就是另一个字符串的长度. for(int i = 0; i <= len1; ++i) dp[i][0] = i; for(int i = 0; i <= len2; ++i) dp[0][i] = i; for(int i = 1; i <= len1; ++i) { for(int j = 1; j <= len2; ++j) { if(s1[i-1] == s2[j-1]) //对齐s1[i-1]和s2[j-1],不需改变 dp[i][j] = dp[i-1][j-1]; else dp[i][j] = min(dp[i-1][j],min(dp[i][j-1],dp[i-1][j-1])) + 1; //s1前缀右对齐,s2前缀右为' ',删除s1第i个字符 -> dp[i-1][j] //s2前缀右对齐,s1前缀右为' ',删除s2第j个字符 -> dp[i][j-1] //s1前缀右对齐,s2前缀右对齐,i、j不一样,替换 -> dp[i-1][j-1] } } return dp[len1][len2]; }
void DP(int x){ for(int i=0;i<=t;i++){ dp[x][i]=p[x].val; } for(int i=0;i<son[x].size();i++){ if(in[son[x][i]]==1) continue; DP(son[x][i]); int lim=val[x][i]; for(int j=t;j>=lim;j--){ for(int k=0;k<=j-lim;k++) dp[x][j]=max(dp[x][j-lim-k]+dp[son[x][i]][k],dp[x][j]); } } return ; }
例题:
题目:hdoj1561The more, The Better(树形dp,依赖背包)
题意:ACboy很喜欢玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中ACboy允许攻克M个城堡并获得里面的宝物。但由于地理位置原因,有些城堡不能直接攻克,要攻克这些城堡必须先攻克其他某一个特定的城堡。你能帮ACboy算出要获得尽量多的宝物应该攻克哪M个城堡吗?
这题的关系就是裸地依赖背包,用树形dp解。
题解:首先,限制条件是选择m个物品,而每个物品最多选一次,跟0-1背包的区别在于有依赖关系,那么这层依赖关系我们可以借助于一个树来解决。借助dfs,从根节点开始dfs,然后直到叶子节点,回朔的时候进行0-1背包dp。
#include <vector> #include <cstring> #include <cstdio> #include <string> #include <algorithm> #include <vector> #define Del(a,b) memset(a,b,sizeof(a)) const int N = 150; using namespace std; int n,m; int dp[N][N],vis[N]; //dp[i][j]表示在节点i,从以i为根节点的子树下选择j个城市的最大价值 int cap[N],val[N]; vector<int> v[N]; void creat(int o) { for(int i=0; i<v[o].size(); i++) { int t=v[o][i]; if(vis[t] == 0 && v[t].size()>0) creat(t); for(int j = m ; j > 1 ; j--) //j>1表示此节点一定要取 0-1背包 { for(int k=1; k<j; k++) //枚举给当前节点的其他子树留多少可选择的城市 dp[o][j]=max(dp[o][j],dp[o][k]+dp[t][j-k]); } } } int main(){ while(cin >> n >> m , n&&m){ m ++; // 加一个根节点 Del(dp,0); for(int i = 1 ; i <= n ; i ++){ int a , b; cin >> a >> b; v[a].push_back(i); for(int j = 1 ; j <= m ; j++) dp[i][j] = b; // 初始化时,每个节点,所有状态都是拿自己一个 } creat(0); for(int i = 0 ; i <= n ; i ++) v[i].clear(); cout << dp[0][m] << endl; } return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。