从课程角度出发:之后我们会面对《数据结构与算法》的课程考试:你将要掌握 链表、栈和队列、八种常见排序的空间时间复杂度和实现、二叉树(哈希树、哈夫曼树、树的搜索)、图论(三种最短路算法)······
从专业角度出发:基础算法是未来我们进行专业算法学习的基础和思维拓展。
for (int i = 0; i < n; i++) {
if (i == n)
printf("%d\n", i);
else
printf("%d ", i);
}
出错特征:WA到死。
double a = 1/3*3;
double b = 1;
if (a == b) {
printf("Yes");
}
治疗方法:
const double eps = 1e-5;
double a = 1/3*3;
double b = 1;
if (abs(a-b) < eps) {
printf("Yes");
}
注意点:eps到底取多少? 一般在1e-5到1e-8之间。有些题目卡eps。(就是莫名其妙的一个wa一个ac)
出错特征:Output Limit Error 或 WA 或 RE 或 TE 或 机器爆炸。
治疗方法:先睡一觉。写出这种代码,你一定是太累了。
治疗方法2:多使用全局变量开大数组
出错特征:差别不大的会WA或TE。差别大的会RE。
出错样例:眼花手抖导致的数组少个0。
治疗方案:数组开的足够大。
#include
using namespace std;
const int MAXN = 1e5+1;
int a[MAXN];
出错特征:部分样例点AC,但是卡了几个WA。
治疗方案:int的取值范围为-2147483648到±2147483648
大概是10个0的样子
NOJ1083
这篇Noj中写的非常明确,大家可以课后对照参考一下各种输入输出。
Compilation Error 代码编译错误
Runtime Error 对应内存越界访问和堆栈溢出
Time Limit Exceeded 对应算法的时间复杂度不够优化
Memory Limit Exceeded 程序超过了题目的内存限制
Wrong Answer 程序输出的答案不正确
顾名思义,枚举便是依次列举出所有可能产生的结果,根据题中的条件对所得的结果进行逐一的判断,过滤掉那些不符合要求的,保留那些符合要求的,也可以称之为暴力算法。
在竞赛中,并不是所有问题都可以使用枚举算法来解决(事实上,只有少数),只有当问题的所有解的个数不太多时,并在我们题目中可以接受的时间内得到问题的解,才可以使用枚举。
int count=0;
for(int i=2;i<n;i++)
if(n%i==0)
count++;
printf("%d\n",count);
记得我刚开始的时候,最喜欢做的就是模拟题,为什么?因为模拟题很少会用到算法,事实也是如此。模拟题考验的是我们的代码实现能力,简单的模拟题基本不用想,就是水题,但是比较难的模拟题,需要我们仔细思考,要寻找出一种能够在代码上相对来说比较好实现并且可以解决这道题的数据结构,这考验的是我们对数据结构的掌握和对题意向代码的转化。模拟题很耗时,只要静下心,考虑到题中的所有坑点,一般模拟题都可以AC。
S (((()()())))
P-sequence 4 5 6 6 6 6
W-sequence 1 1 1 4 5 6
1.p序列:第i个位置之前存在p[i]个左括号,其中i为右括号。(从该括号对的右括号开始往左数,直到最前面的左括号数,就是pi的值。)
2.w序列:第i个右括号所在位置,能和在它左边的第w[i]个左括号匹配
对给出的p数字串,求出对应的s串。串长限制均为20。
#include
using namespace std;
int a[30];
int main(){
int N;
cin >> N;
while(N--){
int n;
cin >> n;
a[0] = 0;
for(int i = 1; i <= n; i++){
cin >> a[i];
int j = i - 1;
while(a[i] - a[j] < i - j) j--;
cout << (i-j) <<' ';
}
cout << endl;
}
return 0;
}
蛇行数的格式如下:
1 2 6 7 15 16
3 5 8 14
4 9 13
10 12
11
现在请你输出蛇行数完整的n*n矩阵
如:输入 5
输出:
1 2 6 7 15
3 5 8 14 17
4 9 13 18 26
10 12 19 25 32
11 20 24 33 41
#include
#include
using namespace std;
int a[1000][1000];
int dir[4][2]={
0,1,1,-1,1,0,-1,1};
int main()
{
memset(a,0,sizeof(a));
int n;
cin>>n;
int cnt=1,x=0,y=0;
a[x][y]=cnt;
while(cnt<=1000){
x=x+dir[0][0];
y=y+dir[0][1];
cnt++;
a[x][y]=cnt;
while(y>0){
x=x+dir[1][0];
y=y+dir[1][1];
cnt++;
a[x][y]=cnt;
}
x=x+dir[2][0];
y=y+dir[2][1];
cnt++;
a[x][y]=cnt;
while(x>0){
x=x+dir[3][0];
y=y+dir[3][1];
cnt++;
a[x][y]=cnt;
}
}
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++)
cout<<a[i][j]<<" ";
cout<<endl;
}
return 0;
}
学习计算机的任何一门语言,都要掌握排序算法,参与竞赛更要掌握排序算法,将来找工作笔试面试的时候都会考到排序算法,所以说排序算法是十分重要的,我们要全面掌握并深入理解。
#include
#include
using namespace std;
const int MAXN=1e5+1;
struct student{
int num;
char name[52];
}
bool cmp1(int a,int b){
return a<b;
}
bool cmp2(student a,student b){
return a.num<b.num;
}
int a[MAXN];
student s[MAXN];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
for(int i=0;i<n;i++)
cin>>s[i].num>>s[i].name;
sort(a,a+n,cmp1);
sort(s,s+n,cmp2);
return 0;
}
数学作为计算机的基础学科,具有重要的基础意义,在很多独立问题上或许本身就存在着数学的最优解答,希望大家能多了解数学定理,多拓宽自己的数学学习面。
深度优先搜索的英文简写是DFS(Depth First Search),属于图论中搜索算法中的一种,它所遵循的搜索策略是尽可能“深”地搜索图。
#include
#include
#include
using namespace std;
const int maxn=100;
bool vst[maxn][maxn]; // 访问标记
int map[maxn][maxn]; // 坐标范围
int dir[4][2]= {
0,1,0,-1,1,0,-1,0}; // 方向向量,(x,y)周围的四个方向
bool CheckEdge(int x,int y) // 边界条件和约束条件的判断
{
if(!vst[x][y] && ...) // 满足条件
return 1;
else // 与约束条件冲突
return 0;
}
void dfs(int x,int y)
{
vst[x][y]=1; // 标记该节点被访问过
if(map[x][y]==G) // 出现目标态G
{
...... // 做相应处理
return;
}
for(int i=0; i<4; i++)
{
if(CheckEdge(x+dir[i][0],y+dir[i][1])) // 按照规则生成下一个节点
dfs(x+dir[i][0],y+dir[i][1]);
}
return; // 没有下层搜索节点,回溯
}
int main()
{
......
return 0;
}
例题1.hrbust 1143
#include
#include
using namespace std;
const int MAXN=1001;
int vis[MAXN][MAXN];
int map[MAXN][MAXN];
int dir[4][2]={
1,0,-1,0,0,1,0,-1};
int ans;
int n,m,p1,p2;
void dfs(int x,int y){
vis[x][y]=1;
ans++;
for(int k=0;k<4;k++){
int tx=x+dir[k][0],ty=y+dir[k][1];
if (tx<=n && ty<=m && tx>0 && ty>0 && map[tx][ty]<=map[p1][p2] && !vis[tx][ty]){
dfs(tx,ty);
}
}
}
int main()
{
while(cin>>n>>m>>p1>>p2){
memset(vis,0,MAXN*MAXN);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>map[i][j];
ans=0;
dfs(p1,p2);
cout<<ans;
}
return 0;
}
队列是一种特殊的线性表,是一种数据结构,它的特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。 它最主要的特点是先进先出。
对队列进行的最常见的操作分别是:1、插入,2、删除。对队列的操作就好像一群人在排队,排在离柜台最近的人称之为“队头”,离柜台最远的人称之为“队尾”。每新来一个人需要排队(假设我们都是有素质的人,不插队),新来的人应该排在队尾,对吧?这就是插入操作。而柜台的办公人员会给队头的人先办公,然后依次进行,队头的人被服务完,他便会离开,这就是删除操作。
如何在代码中定义队列?
queue< int> q; 定义一个整型队列
q.push(a); 将a元素插入到q队列的队尾
q.pop(); 将q队列的队头从队列中删除
q.front() q队列的队头元素
q.back() q队列的队尾元素
q.size() q队列的大小
q.empty() 返回值是1代表队列是空,否则队列不为空
(如果你问一个人push的反义词是什么,他回答是pop的话,他一定是一位合格的程序员。)
广度优先搜索的英文简写是BFS(Breadth First Search),属于图论中搜索算法中的一种,它所遵循的搜索策略是尽可能“广”地搜索图。
#include
#include
#include
#include
using namespace std;
const int maxn=100;
bool vst[maxn][maxn]; // 访问标记
int dir[4][2]= {
0,1,0,-1,1,0,-1,0}; // 方向向量
struct State // BFS 队列中的状态数据结构
{
int x,y; // 坐标位置
int Step_Counter; // 搜索步数统计器
};
State a[maxn];
bool CheckState(State s) // 约束条件检验
{
if(!vst[s.x][s.y] && ...) // 满足条件
return 1;
else // 约束条件冲突
return 0;
}
void bfs(State st)
{
queue <State> q; // BFS 队列
State now,next; // 定义2 个状态,当前和下一个
st.Step_Counter=0; // 计数器清零
q.push(st); // 入队
vst[st.x][st.y]=1; // 访问标记
while(!q.empty())
{
now=q.front(); // 取队首元素进行扩展
if(now==G) // 出现目标态,此时为Step_Counter 的最小值,可以退出即可
{
...... // 做相关处理
return;
}
for(int i=0; i<4; i++)
{
next.x=now.x+dir[i][0]; // 按照规则生成下一个状态
next.y=now.y+dir[i][1];
next.Step_Counter=now.Step_Counter+1; // 计数器加1
if(CheckState(next)) // 如果状态满足约束条件则入队
{
q.push(next);
vst[next.x][next.y]=1; //访问标记
}
}
q.pop(); // 队首元素出队
}
return;
}
int main()
{
......
return 0;
}
#include
#include
using namespace std;
int ans,step;
int st,end;
const int MAXN=1e5+1;
int vis[MAXN];
int queue[MAXN];
bool checke(int n)
{
if (n>=0 && n<=MAXN && vis[n]==-1)
return true;
else
return false;
}
int bfs(int st,int end){
int front=0,back=0,now;
queue[back++]=st;
vis[st]=0;
while(front<=back){
now = queue[front++];
if(now==end) return vis[end];
if(checke(now-1)){
vis[now-1]=vis[now]+1;
queue[back++]=now-1;
}
if(checke(now+1)){
vis[now+1]=vis[now]+1;
queue[back++]=now+1;
}
if(checke(now*2)){
vis[now*2]=vis[now]+1;
queue[back++]=now*2;
}
}
}
int main()
{
while(cin>>st>>end){
ans=0;
memset(vis,-1,MAXN);
cout<<bfs(st,end)<<endl;
}
return 0;
}
#include
#include
#include
using namespace std;
int a[6][5]={
0,0,1,0,0,
0,1,0,0,0,
0,1,0,1,1,
0,1,0,0,0,
0,0,0,1,0,
0,0,0,0,0
};
int dir[4][2]={
0,1,0,-1,1,0,-1,0}; //dir控制方向
struct State{
//用state保存该点以及当前步数
int x,y=0;
int stepCount=0;
};
int bfs(State st,State _end) // bfs核心部分
{
queue <State> q; //建立队列
State now,next;
q.push(st); //将起点入队
a[st.x][st.y]=1; //入队后重置该点为走过的点(此句为起点单独列出)
while(!q.empty())
{
now=q.front();
q.pop();
if(now.x==_end.x && now.y==_end.y) //到达终点,返回步数
return now.stepCount;
for(int i=0;i<4;i++)
{
next.x=now.x+dir[i][0];
next.y=now.y+dir[i][1];
if(next.x<0 || next.y<0 || next.x>=6 || next.y>=5 || a[next.x][next.y]==1)
continue;
a[next.x][next.y]=1;
next.stepCount=now.stepCount+1;
q.push(next);
/* for(int j=0;j<6;j++)
{
for(int k=0;k<5;k++)
cout<
}
}
}
int main()
{
State beg,e;
beg.x=0,beg.y=0,beg.stepCount=0;
e.x=0,e.y=4;
cout<<bfs(beg,e);
return 0;
}
求一个图中两个点之间最短距离的最快的一种方法,要求路径的花费不能有负的。
#include
#include
using namespace std;
MAXN=100;
int dis[MAXN]; //与起点的距离
int vis[MAXN]; //访问标记
int a[MAX][MAXN] //a[m][n]为m到n之间的距离
void Dij(int n) //默认起点为1
{
memset(dis,MAXN,sizeof(dis));
vis[1]=1;
dis[1]=0;
for(int i=1;i<=n;i++){
int k=0;
for(int j=1;j<=n;j++)
if(!vis[j]&&(k==0 || dis[j]<dis[k]))
k=j
v[k]=1;
for(int j=1;j<=n;j++)
if(!vis[j]&&dis[k]+a[k][j]<dis[j])
dis[j]=dis[k]+a[k][j];
}
}
(了解:Dijkstra优化)
#include
#include
#include
#include
#include
using namespace std;
const int INF=0x3f3f3f3f;
const int MAXN=100001;
struct qnode{
int v,c; //v代表起点,c代表当前路径长
qnode(int _v=0,int _c=0):v(_v),c(_c){
}
bool operator < (const qnode &r) const //重载来排序优先队列
{
return c>r.c;
}
};
struct Edge{
//邻接矩阵
int v,cost; //v代表指向的点,cost代表路径
Edge(int _v=0,int _cost=0):v(_v),cost(_cost){
}
};
vector<Edge> E[MAXN]; //E[i][j]代表从i指向j,E[i][j].cost为两者间路径, E[i][j].v代表指向的点j。
bool vis[MAXN]; //是否访问标志
int dist[MAXN]; //存放距离,起点到每个点的最短距离
void Dijkstra(int n,int start) //n为结点个数,start为选取的起点
{
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++)
dist[i]=INF;
priority_queue<qnode> que;
while(!que.empty()) que.pop();
dist[start]=0;
que.push(qnode(start,0));
qnode tmp;
while(!que.empty())
{
tmp=que.top();
que.pop();
int u=tmp.v; //u为当前队首作为起点
if(vis[u]) continue;
vis[u]=true;
for(int i=0;i<E[u].size();i++) //E[u].size()等价于节点数
{
int v=E[u][i].v; //v为指向的下一个节点
int cost=E[u][i].cost; //cost为当前起点u到目标点v的已知长度
if(!vis[v]&&dist[v]>cost+dist[u]) //比较起点直接到v点,和先到u点再到v点的路径
{
dist[v]=dist[u]+cost;
que.push(qnode(v,dist[v])); ///将目标点塞入队列,进行下一次松弛
}
}
}
}
void addedge(int u,int v,int w) //用于初始化邻接矩阵
{
E[u].push_back(Edge(v,w));
}
int main()
{
/* 2
P1--->P3
1 ↓ ↓4
P2--->P4
4
*/
int n=4;
int v,w;
freopen("in.txt","r",stdin); //输入输出分别在in.txt和out.txt中;
freopen("out.txt","w",stdout);
for(int i=1;i<=n;i++)
{
addedge(i,0,-1); //使得开始点为1,而不是0。
for(int j=1;j<=n;j++)
{
cin>>w;
if(w==-1) //无限用-1输入
addedge(i,j,INF);
else
addedge(i,j,w);
}
}
/* for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cout<
Dijkstra(4,1);
for(int i=1;i<=4;i++)
cout<<dist[i]<<' ';
return 0;
}
/*
in.txt: 0 1 2 -1
1 0 -1 4
2 -1 0 4
-1 4 4 0
out.txt标准答案: 0 1 2 5
*/
KMP是一种在线性时间内能处理两个字符串的包含关系的算法,例如求一个字符串里有没有另一个字符串,一个字符串里有几个另一个字符串
#include
#include
using namespace std;
int next[101];
void getNext(int* next,char* p){
int i,n,k;
n=strlen(p);
next[1]=next[0]=0;
k=0;
for(i=2;i<=n;i++){
for(;k!=0&&p[k]!=p[i-1];k=next[i]);
if(p[k]==p[i-1])k++;
next[i]=k;
}
}
void kmp(char* text,char* p,int* next)
{
int len_t,len_p,s,q;
len_t=strlen(text);
len_p=strlen(p);
q=s=0;
// q表示上次迭代匹配了多少字符,s表示从总字符的第几位开始
while(s<len_t){
for(q=next[q];q<len_p&&p[q]==text[s];q++,s++);
if(q==0)s++;
else if (q==len_p){
cout<<"匹配成功,在原文的第"<<s-len_p+1<<"处开始。"<<endl;
// q=0; 加上则为不可重叠
}
}
}
int main()
{
char p[]="cac";
char text[]="cacaca";
getNext(next,p);
kmp(text,p,next);
return 0;
}
#include
#include
using namespace std;
const int MAXN = 1e5+1;
char S[MAXN],T[MAXN];
int P[MAXN];
void manacher(char S[], int len)
{
int k = 0;
T[k++]='$',T[k++]='#';
for(int i=0;i < len; i++)
{
T[k++]=S[i];
T[k++]='#';
}
T[k]='0';
int r=0,c=0; //r代表当前对称的最远距离,c代表该对称的中心
for(int i = 0;i < k; i++)
{
int &p=P[i];
p = r > i ? min(P[2*c-i],r-i) : 0; //P[2*c-i] --> 对称点x + i = 2*c 对称点坐标为P[2*c-i]
while(T[i+p+1]==T[i-p-1]) p++;
if(i + p > r) {
r = i + p; c = i;} //如果i+p>r 有更大的扩展范围,则把i设为新中心,r为新半径。
}
/* for(int i=0;i
// 其中原字符串的开头下标 = (i - P[i]) / 2 只要返回 开头 到 开头+P[i]-1 之间的字符串
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>S[i];
}
manacher(S,n);
return 0;
}
字典树,又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来节约存储空间,最大限度地减少无谓的字符串比较,查询效率比哈希表高。
字典树与字典很相似,当你要查一个单词是不是在字典树中,首先看单词的第一个字母是不是在字典的第一层,如果不在,说明字典树里没有该单词,如果在就在该字母的孩子节点里找是不是有单词的第二个字母,没有说明没有该单词,有的话用同样的方法继续查找.字典树不仅可以用来储存字母,也可以储存数字等其它数据。
#include
#include
using namespace std;
const int MAX=26,base='a';
char s1[12],ss[12];
struct Trie{
int num;
bool flag;
Trie *son[MAX];
Trie()
{
num=1;flag=false;
memset(son,NULL,sizeof(son));
}
};
Trie *NewTrie()
{
Trie *temp = new Trie;
return temp;
}
void Insert(Trie *root,char *s)
{
Trie *temp = root;
while(*s){
if(temp->son[*s-base]==NULL)
temp->son[*s-base]=NewTrie();
else
temp->son[*s-base]->num++;
temp=temp->son[*s-base];
s++;
}
temp->flag=true;
}
int Search(Trie *root,char *s)
{
Trie *temp = root;
while(*s){
if(temp->son[*s-base]==NULL) return 0;
temp = temp->son[*s-base];
s++;
}
return temp->num;
}
int main()
{
Trie *root = NewTrie();
root->num=0;
while(gets(s1)&&strcmp(s1,"")!=0)
{
if(strcmp(s1," ")==0)
break;
Insert(root,s1);
}
while(cin>>ss)
{
int ans=Search(root,ss);
cout<<ans<<endl;
}
return 0;
}
————————————————————————————————————————————
——by 创新实践部 钱舟毅
动态规划是一种常用的分析办法,其分析的思维可以推广到许多的问题上去但是动态规划是具有使用条件的,主要是以下三点
最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。
有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)
1.分析最优解的性质,并刻画其结构特征。
2.递归的定义最优解。
3.以自底向上或自顶向下的记忆化方式(备忘录法)计算出最优值
4.根据计算最优值时得到的信息,构造问题的最优解
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 5 1
对于一个斐波那契数列,我们可以得到一个常用的递推公式
a[i][j] = a[i-1][j-1]+a[i-1][j]
通过这个表达式我们就可以通过小的子问题去求出最后我们想要的结果
接下来给出dp算法的基本框架(不是正确的代码语法)
for(j=1; j<=m; j=j+1) // 第一个阶段
xn[j] = 初始值;
for(i=n-1; i>=1; i=i-1)// 其他n-1个阶段
for(j=1; j>=f(i); j=j+1)//f(i)与i有关的表达式
xi[j]=j=max(或min){
g(xi-1[j1:j2]), ......, g(xi-1[jk:jk+1])};
t = g(x1[j1:j2]); // 由子问题的最优解求解整个问题的最优解的方案
print(x1[j1]);
for(i=2; i<=n-1; i=i+1)
{
t = t-xi-1[ji];
for(j=1; j>=f(i); j=j+1)
if(t=xi[ji])
break;
}
从上面的框架我们可以知道,dp算法的本质是将整个问题从最小的子问题从上而下不断进行计算,当我们需要第3行第2列的元素时,我们只需要从第2行第1列和第2行第2列的单元格进行取值,而这些元素已经在之前计算过了,所以我们可以直接算出结果
来看一个生活中常见的问题吧
分析一波,面对每个物品,我们只有选择拿取或者不拿两种选择,不能选择装入某物品的一部分,也不能装入同一物品多次。
解决办法:声明一个 大小为 m[n][c]的二维数组,m[i][j]表示在面对第 i 件物品,且背包容量为 j 时所能获得的最大价值 ,那么我们可以很
容易分析得出 m[i][j] 的计算方法,
(1)j < w[i] 的情况,这时候背包容量不足以放下第 i 件物品,只能选择不拿
m[ i ][ j ] = m[ i-1 ][ j ]
(2)j>=w[i] 的情况,这时背包容量可以放下第 i 件物品,我们就要考虑拿这件物品是否能获取更大的价值。
如果拿取,m[ i ][ j ]=m[ i-1 ][ j-w[ i ] ] + v[ i ]。 这里的m[ i-1 ][ j-w[ i ] ]指的就是考虑了i-1件物品,背包容量为j-w[i]时的最大价值,也是相当于为第i件物品腾出了w[i]的空间。
如果不拿,m[ i ][ j ] = m[ i-1 ][ j ] , 同(1)
究竟是拿还是不拿,自然是比较这两种情况那种价值最大。
由此可以得到状态转移方程:
if(j>=w[i])
m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);
else
m[i][j]=m[i-1][j];
在使用动态规划算法求解0-1背包问题时,使用二维数组m[i][j]存储背包剩余容量为j,可选物品为i、i+1、……、n时0-1背包问题的最优值。绘制
价值数组v = {8, 10, 6, 3, 7, 2},
重量数组w = {4, 6, 2, 2, 5, 1},
背包容量C = 12时对应的m[i][j]数组。
0 1 2 3 4 5 6 7 8 9 10 11 12
1 0 0 0 8 8 8 8 8 8 8 8 8
2 0 0 0 8 8 10 10 10 10 18 18 18
3 0 6 6 8 8 14 14 16 16 18 18 24
4 0 6 6 9 9 14 14 17 17 19 19 24
5 0 6 6 9 9 14 14 17 17 19 21 24
6 2 6 8 9 11 14 16 17 19 19 21 24
(第一行和第一列为序号,其数值为0)
如m[2][6]
在面对第二件物品,背包容量为6时我们可以选择不拿,那么获得价值仅为第一件物品的价值8,如果拿,就要把第一件物品拿出来,放第二件物品,价值10,那我们当然是选择拿。m[2][6]=m[1][0]+10=0+10=10
;依次类推,得到m[6][12]
就是考虑所有物品,背包容量为C时的最大价值。
#include
#include
using namespace std;
const int N=15;
int main()
{
int v[N]={
0,8,10,6,3,7,2};
int w[N]={
0,4,6,2,2,5,1};
int m[N][N];
int n=6,c=12;
memset(m,0,sizeof(m));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=c;j++)//双重循环控制正向递推
{
if(j>=w[i])
m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);//递推关系式
else
m[i][j]=m[i-1][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=c;j++)
{
cout<<m[i][j]<<' ';
}
cout<<endl;
}//打印输出二维表格,同时也打印出需要的单元数据
return 0;
}