花點時間整理下以前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 一樣的 多了些條件 記錄區間什麼的
/***********************************************************************************/