花点时间整理下以前A掉的水题 , 巩固下基础 ,也希望能再细节处理上有所提高
可以用动态规划来解决的问题 要满足无后效性(DAG)、最优子结构
【无特殊说明都是JOJ的题】
/***********************************************************************************/
2526: medic采药问题, 经典01背包,不必装满
1维空间,用组合数学的生成函数思想更好理解一些
#include <cstdio> #include <string.h> const int maxn=102; int w[maxn],v[maxn],ans[1005]; int main () { int t,m,i,j; while (scanf("%d%d",&t,&m)==2) { for (i=0 ; i<m ; ++i) scanf("%d%d",w+i,v+i); memset (ans,0,sizeof(ans)); for (i=0 ; i<m ; i++)//0...N for (j=t ; j>=w[i] ; --j)//V...cost { ans[j]>?=ans[j-w[i]]+v[i]; } //for (i=0 ; i<=t ; i++) printf("%d\n",ans[t]); } return 0; }
/***********************************************************************************/
1424 1583 2201 1176 可看作质量为1的,完全背包计数问题,注意初始化,理解成生成函数更好些
#include<stdio.h> #include<memory.h> using namespace std; const int maxn=7500; int c[5]={1,5,10,25,50}; int v; int f[maxn]; int main() { memset(f,0,sizeof(f)); f[0]=1; for(int i=0;i<5;i++) for(int j=c[i];j<=maxn;j++) { f[j]+=f[j-c[i]];//重要 } while(scanf("%d",&v)==1) { printf("%d\n",f[v]); } return 0; }
/***********************************************************************************/
POJ 2392 多重背包 模板
#include <cstdio> #include <cstring> #include <algorithm> #define max(a,b) (a>b?a:b) using namespace std; const int maxn=500; int dp[5][40050]; struct Item { int h,a,c; }blo[maxn]; bool cmp(const Item & a , const Item & b) { return a.a<b.a; } void O1Pack(int f[] , int c , int w , int V) { for (int v=V ; v>=c ; --v) f[v]=max(f[v-c]+w , f[v]); } void ComPack(int f[] , int c , int w , int V) { for (int v=c ; v<=V ; ++v) f[v]=max(f[v-c]+w , f[v]); } void MulPack(int f[] , int c , int w , int m , int V) { if(c*m>=V) { ComPack(f , c , w , V); return ; } int k=1; while (k<m) { O1Pack(f , k*c , k*w , V); m-=k; k*=2; } O1Pack(f , c*m , w*m , V); } int main () { int c,w,m; while (~scanf("%d",&m)) { memset (dp , 0 , sizeof(dp)); for (int i=0 ; i<m ; ++i) { scanf("%d%d%d",&blo[i].h , &blo[i].a , &blo[i].c); } sort(blo , blo+m , cmp); for (int i=0 ; i<m ; ++i) { MulPack(dp[0] , blo[i].h , blo[i].h , blo[i].c , blo[i].a); } int ans=0; for (int i=0 ; i<=blo[m-1].a ; ++i) ans=max(dp[0][i],ans); printf("%d\n",ans); } return 0; }
/***********************************************************************************/
HDU 1561泛化背包
/**O(n*V)泛化背包 算法来自论文【浅谈几类背包题】 4686406 2011-10-01 14:23:10 Accepted 1561 0MS 372K 1776 B G++ Geners **/ #include <cstdio> #include <cstring> #define max(a,b) (a>b?a:b) const int maxn=205; struct Edge { int v,next; }edge[maxn]; int head[maxn],cnt; void addedge(int u,int v) { edge[cnt].v=v; edge[cnt].next=head[u]; head[u]=cnt++; } int w[maxn],dp[maxn][maxn],node; int n,m; void dfs(int u , int c) { for (int p=head[u] ; ~p ; p=edge[p].next) { int &v=edge[p].v; for (int j=0 ; j<c ; ++j) dp[v][j]=dp[u][j]+w[v];///取儿子的时候,父亲必须取,因此直接将父亲放入儿子, dfs(v , c-1);///更新儿子 for (int j=1 ; j<=c ; ++j)///求2个有交集的泛化物品的并 dp[u][j]=max(dp[u][j] , dp[v][j-1]);///更新儿子后,再赋值给父亲 }///对于树的叶结点 我们可以把他处理成01Pack } int main () { while (scanf("%d%d",&n,&m),(n||m)) { memset(head , -1 , sizeof(head)); cnt=0; for (int i=1 ; i<=n ; ++i) { scanf("%d %d" , &node , w+i); addedge(node , i); } w[0]=0; memset(dp , 0 , sizeof(dp)); dfs(0 , m); printf("%d\n",dp[0][m]); } return 0; }
/***********************************************************************************/
2529 合唱 最长单调序列变形
用OPT1存上升的,OPT2存下降的,则第I个人的最大为OPT1[I]+OPT[I] -1 时间n^2 空间n
因为每个数都包含自己本身,所以初始化全为1。
/***********************************************************************************/
2511 经典数塔问题,从下往上搜
opt[i][j]=num[i][j];
for i m..0
for j i..0
opt[i][j]+=opt[i+1][j]>?opt[i+1][j+1];
/***********************************************************************************/
1826 三角剖分 有公式的 可以推
f[2]=1;f[3]=1; f[i]=4*f[i-1]-6*f[i-1]/(i-1);
/***********************************************************************************/
1968 最长公共子序列 时间 n^2 空间n^2
阶段不是很明显,opt[i][j]表示从起点开始 str1长度为i,str2长度为j的最长公共子序列
#include <stdio.h> #include <memory.h> #include <string.h> const int maxn=200; char str1[maxn],str2[maxn]; int opt[maxn][maxn]; int same(int i,int j) { if(str1[i]==str2[j]) return 1; return 0; } int main () { int i,j; while (scanf("%s%s",str1,str2)!=EOF) { memset (opt,0,sizeof(opt)); opt[1][1]=same(0,0); int len1=strlen(str1); int len2=strlen(str2); int ans=0; for (i=1 ; i<=len1 ; i++) for (j=1 ; j<=len2 ; j++) { opt[i][j]=opt[i][j-1]; if(opt[i][j]<opt[i-1][j])opt[i][j]=opt[i-1][j]; if(opt[i][j]<(opt[i-1][j-1]+same(i-1,j-1))) opt[i][j]=(opt[i-1][j-1]+same(i-1,j-1)); } printf("%d\n",opt[len1][len2]); } return 0; }
/***********************************************************************************/
1995 最大子段和 时间n
用sum寄存 前缀和,当sum<0时,清零,同时要记录扫描过程sum的最大值 以及数值的最大值,因为有可能有全是负数的情况,但这时sum的最大值默认为0
2058 一样的 多了些条件 记录区间什么的
/***********************************************************************************/