花点时间整理下以前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 一样的 多了些条件 记录区间什么的
/***********************************************************************************/