(背包问题系列)
1.poj 3624
01背包问题,这题的难点是,如果直接使用二维dp数组,内存会不够用。所以要想办法转换成1维dp。
其时计算的时候,复杂度是一样的(两重循环),不过在存储空间上,我们可以只考虑“容量一定钱最多”的情况。这时,我们只需遍历每种物品,看是否要选中它,从而来进行计算即可。
迁移方程:dp[j] = max(dp[j-w[i]])+v[i], i = 1,...,n.
#include "stdafx.h"
#include<cstdio>
#include<iostream>
#include<algorithm>
#define N_MAX 3405
#define M_MAX 12885
using namespace std;
int dp[M_MAX],datas[N_MAX][2];//(weights,values)
int N, M;
int main()
{
scanf_s("%d%d",&N,&M);
for (int i = 0; i < N; ++i)//物品从1开始计数
{
scanf_s("%d%d",&(datas[i+1][0]),&(datas[i+1][1]));
}
memset(dp, 0, sizeof(int)*(M + 1));
for (int i = 1; i <= N; ++i)
{
for (int j = M; j >= datas[i][0] ; --j)
{
dp[j] = max(dp[j],dp[j-datas[i][0]]+datas[i][1]);
}
}
printf("%d\n",dp[M]);
return 0;
}
2.poj 3628
一道看上去不像背包问题的题目。
如果把牛的高度看成是它的价值,也看成是它的权重,那么dp数组存放的就是在权重之和不超过i的时候,价值最多是多少。感觉上它是遍历了奶牛的各种组合。
#include "stdafx.h"
#include<cstdio>
#include<iostream>
#include<algorithm>
#define N_MAX 21
#define M_MAX 1000001
using namespace std;
int dp[N_MAX*M_MAX],datas[N_MAX];//(weights,values)
int N, B,sums;
int main()
{
scanf_s("%d%d",&N,&B);
sums = 0;
for (int i = 0; i < N; ++i)//物品从1开始计数
{
scanf_s("%d",&(datas[i+1]));
sums += datas[i + 1];
}
memset(dp, 0, sizeof(int)*(B + 1));
for (int i = 1; i <= N; ++i)
{
for (int j = sums; j >= datas[i] ; --j)
{
dp[j] = max(dp[j],dp[j-datas[i]]+datas[i]);
}
}
for (int i = B; i <= sums; ++i)
{
if (dp[i] >= B)
{
printf("%d\n", dp[i]-B);
break;
}
}
return 0;
}
3.poj 1384
完全背包问题:这里加入了一个额外的判断条件,就是一定要使得重量完全被利用上。
我很好奇的是,为啥这题的动态规划部分的代码跟poj 3624一样。。。(不一样的地方在于,第二重循环的方向,完全背包是顺序遍历的,而01背包是逆序遍历的)
我采用了所谓的k个循环的方式,考虑每种硬币的取的个数,结果TLE了。。。
#include "stdafx.h"
#include<iostream>
#include<cstdio>
#include<algorithm>
#define N_MAX 505
#define W_MAX 10005
#define P_MAX 50005*N_MAX
using namespace std;
int dp[W_MAX],datas[N_MAX][2];//0:value,1,weight
int T, E ,F,N,temp;
int main()
{
scanf_s("%d",&T);
for (int i = 0; i < T; ++i)
{
scanf_s("%d%d", &E, &F);
scanf_s("%d", &N);
for (int i = 1; i <= N; ++i)//count from 1 to N
{
scanf_s("%d%d",&(datas[i][0]),&(datas[i][1]));
}
for (int i = 1; i <= F - E; ++i)
dp[i] = P_MAX;
dp[0] = 0;
for (int i = 1; i <= N; ++i)
{
for (int j = datas[i][1]; j <= F - E; ++j)
{
dp[j] = min(dp[j], dp[j - datas[i][1]] + datas[i][0]);
}
}
//for (int i = 1; i <= F - E; ++i)
// cout << dp[i] << ' ';
//cout << endl;
if (dp[F - E] == P_MAX)
printf("This is impossible.\n");
else
printf("The minimum amount of money in the piggy-bank is %d.\n",dp[F-E]);
}
return 0;
}
貌似完全背包的问题一般都是采用一维dp来解的,因为二维dp往往需要三重循环,因而非常容易出现TLE的情况。
这种优化的方式,就是滚动数组。
4.百练 2754(八皇后问题)
打表解决。
#include "stdafx.h"
#include<iostream>
#include<cstdio>
using namespace std;
int tables[92] = { 15863724,
16837425,
17468253,
17582463,
24683175,
25713864,
25741863,
26174835,
26831475,
27368514,
27581463,
28613574,
31758246,
35281746,
35286471,
35714286,
35841726,
36258174,
36271485,
36275184,
36418572,
36428571,
36814752,
36815724,
36824175,
37285146,
37286415,
38471625,
41582736,
41586372,
42586137,
42736815,
42736851,
42751863,
42857136,
42861357,
46152837,
46827135,
46831752,
47185263,
47382516,
47526138,
47531682,
48136275,
48157263,
48531726,
51468273,
51842736,
51863724,
52468317,
52473861,
52617483,
52814736,
53168247,
53172864,
53847162,
57138642,
57142863,
57248136,
57263148,
57263184,
57413862,
58413627,
58417263,
61528374,
62713584,
62714853,
63175824,
63184275,
63185247,
63571428,
63581427,
63724815,
63728514,
63741825,
64158273,
64285713,
64713528,
64718253,
68241753,
71386425,
72418536,
72631485,
73168524,
73825164,
74258136,
74286135,
75316824,
82417536,
82531746,
83162574,
84136275};
int case_num, index;
int main()
{
scanf_s("%d",&case_num);
for (int i = 0; i < case_num; ++i)
{
scanf_s("%d",&index);
printf("%d\n",tables[index-1]);
}
}
//int tables[100][8];
//int chessboards[8][8];
//int counter;
//
//void put_chess(int pos)
//{
// if (pos >= 8)
// {
// counter++;
// return;
// }
//
// for (int i = 0; i < 8; ++i)
// {
// if (chessboards[i][pos] == -1)
// {
// chessboards[i][pos] = pos;
// for (int j = counter; j < 92; ++j)
// {
// tables[j][pos] = i+1;
// }
//
// for (int xi = 0; xi < 8; ++xi)
// {
// for (int yi = 0; yi < 8; ++yi)
// {
// if (chessboards[xi][yi] == -1 &&
// (xi == i || yi == pos || abs(xi - i) == abs(yi - pos)))
// chessboards[xi][yi] = pos;
// }
// }
// put_chess(pos + 1);
//
// for (int xi = 0; xi < 8; ++xi)
// {
// for (int yi = 0; yi < 8; ++yi)
// {
// if (chessboards[xi][yi] == pos)
// chessboards[xi][yi] = -1;
// }
// }
// }
// }
//}
//
//int main()
//{
// counter = 0;
// memset(chessboards, -1, sizeof(chessboards));
// put_chess(0);
// for (int i = 0; i < 92; ++i)
// {
// for (int j = 0; j < 8; ++j)
// {
// cout << tables[i][j];
// }
// cout <<","<< endl;
// }
// return 0;
//}
5.hdu 2553(八皇后问题的简化版)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
using namespace std;
int tables[1000][10];
int chessboards[10][10],res[10];
int counter,n;
void put_chess(int pos,int N)
{
if (pos >= N)
{
counter++;
return;
}
for (int i = 0; i < N; ++i)
{
if (chessboards[i][pos] == -1)
{
chessboards[i][pos] = pos;
for (int j = counter; j < 1000; ++j)
{
tables[j][pos] = i+1;
}
for (int xi = 0; xi < N; ++xi)
{
for (int yi = 0; yi < N; ++yi)
{
if (chessboards[xi][yi] == -1 &&
(xi == i || yi == pos || abs(xi - i) == abs(yi - pos)))
chessboards[xi][yi] = pos;
}
}
put_chess(pos + 1,N);
for (int xi = 0; xi < N; ++xi)
{
for (int yi = 0; yi < N; ++yi)
{
if (chessboards[xi][yi] == pos)
chessboards[xi][yi] = -1;
}
}
}
}
}
int main()
{
for (int i = 0; i < 10; ++i)
{
counter = 0;
memset(chessboards, -1, sizeof(chessboards));
put_chess(0,i+1);
res[i] = counter;
}
while (scanf("%d", &n), n)
{
printf("%d\n",res[n-1]);
}
return 0;
}
6.poj 2262检查质数
两个点:(1)第一重循环中的i*i,i+=2
(2)如果是bool[MAX],那么第一重循环中的i*i,i++;第二重循环中的j从2开始,j*i <MAX。
#include "stdafx.h"
#include<cstdlib>
#include<cmath>
#include<cstdio>
#include<iostream>
#include<vector>
#define NUM 1000000
using namespace std;
int num;
bool IsPrime(int datas)
{
for (int i = 3; i*i <= datas; i += 2)
{
if (datas%i == 0)
return false;
}
return true;
}
int main()
{
while (scanf_s("%d", &num) != EOF,num)
{
for (int i = 3; i <= num / 2; i += 2)
{
if (IsPrime(i) && IsPrime(num - i))
{
printf("%d = %d + %d\n",num,i,num-i);
break;
}
}
}
return 0;
}
7.poj 3984
bfs走迷宫
这里谈一下编译器的问题:
一般常用的编译器有g++,gcc,c++.考场一般都有g++和gcc,前者是兼容c++与c的,是最常用的编译器。
但是该编译器的一个问题(对于写惯了visual studio的人来说),就是很多库函数的位置与vc++编译器不同。
比如memset,strlen,strstr等和字符串处理相关的函数在库<cstring>中,abs在<cstdlib>中,fabs,sin,sqrt等在<cmath>中。
#include "stdafx.h"
#include<stack>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;
int orients[4][2] = {1,0,0,1,-1,0,0,-1};
int matrix[5][5];
bool visits[5][5];
int signal,res;
stack<vector<int> >coor;
bool judge(int nx, int ny)
{
return ((nx >= 0 && nx < 5) && (ny >= 0 && ny < 5));
}
int dfs(int x_cor,int y_cor)
{
if (x_cor == 4 && y_cor == 4)
{
res++;
return 1;
}
if (visits[x_cor][y_cor] == false && !signal)
{
visits[x_cor][y_cor] = true;
res++;
for (int i = 0; i < 4; ++i)
{
int nx = x_cor + orients[i][0];
int ny = y_cor + orients[i][1];
if (judge(nx, ny) && matrix[nx][ny] == 0)
{
int signs = dfs(nx, ny);
if (signs)
{
vector<int>temp;
temp.push_back(nx);
temp.push_back(ny);
coor.push(temp);
return 1;
}
}
}
}
return 0;
}
int main()
{
for (int i = 0; i < 5; ++i)
{
for (int j = 0; j < 5; ++j)
{
scanf("%d",&(matrix[i][j]));
}
}
signal = res = 0;
memset(visits, false, sizeof(visits));
dfs(0, 0);
printf("(%d, %d)\n",0,0);
while (coor.size())
{
vector<int>temp = coor.top();
coor.pop();
printf("(%d, %d)\n", temp[0], temp[1]);
}
return 0;
}
8.hdu 2544(dijkstra模版题)
#include<queue>
#include<functional>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define EDGE_NUM 10005//边的数目上限
#define NODE_NUM 105//节点数目上限
#define MAX_INT 1000000//边的权重上限
int edge_index[NODE_NUM],min_dist[NODE_NUM];//前者用来存储以节点i为出发点的各条边
//邻接链表的初始边的序号;后者用来存储源节点到各个节点的最短距离
int edge_num,node_n,edge_n,sou,tar,wei;//存储边的数目,同时也可作为边的序号
typedef pair<double,int>P;//前面是每个点的当前的距离,后面则是各个点的序号,
//用这种方式存储便于放在队列中进行比较
struct Edge//采用类的数组来存储边,这种邻接链表的存储边的方式在稀疏表中存储效率要高于邻接矩阵,邻接矩阵适合存储密集图
{
int source,target,weight;
int next;//记录每个节点的上一条同样以source为出发点的边的边序号
}edges[EDGE_NUM*2];//这里注意需要将边数乘以2,因为图是无向图;如果是有向图,则
//无需乘法操作
void add_edge(int sta_p,int end_p,int wei)//添加边到边的数组
{
edges[edge_num].source = sta_p;
edges[edge_num].target = end_p;
edges[edge_num].weight = wei;
edges[edge_num].next = edge_index[sta_p];
edge_index[sta_p] = edge_num++;//因此这里需要更新这个初始边的序号
}
void dijkstra(int sta)
{
priority_queue<P,vector<P>,greater<P> >graphs;
min_dist[sta] = 0;
graphs.push(make_pair(0,sta));
while(graphs.size())
{
P temp = graphs.top();
graphs.pop();
if(temp.first > min_dist[temp.second])
continue;
for(int i = edge_index[temp.second];i != -1;i = edges[i].next)
{
int node_weight = edges[i].weight;
int node_target = edges[i].target;
if(min_dist[node_target] > min_dist[temp.second]+node_weight)
{
min_dist[node_target] = min_dist[temp.second]+node_weight;
graphs.push(make_pair(min_dist[node_target],node_target));
}
}
}
}
int main()
{
//get the nubmer of nodes and roads......
while(scanf("%d%d",&node_n,&edge_n),node_n)
{
edge_num = 0;
memset(edge_index,-1,sizeof(edge_index));
for(int i = 1;i <= node_n;++i)
{
min_dist[i] = MAX_INT;
}
//add_edges.....
for(int i = 0;i < edge_n;++i)
{
scanf("%d%d%d",&sou,&tar,&wei);
add_edge(sou,tar,wei);
add_edge(tar,sou,wei);
}
dijkstra(1);
printf("%d\n",min_dist[node_n]);
}
}
9.hdu 1863最小生成树模版题
#include<queue>
#include<functional>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#define NODE_NUM 100
#define EDGE_NUM NODE_NUM*(NODE_NUM-1)/2
using namespace std;
int N,R,num_edge,node_counter,sums;
struct Edge
{
int source,target,weight;
}edges[EDGE_NUM];
int parents[NODE_NUM];
const bool cmp(const Edge &a,const Edge &b)
{
return a.weight < b.weight;
}
int find_parent(int x)
{
if(x != parents[x])
parents[x] = find_parent(parents[x]);
return parents[x];
}
int merge(int x,int y)
{
int sig = 0;
int px = find_parent(x),
py = find_parent(y);
if(px != py)
{
parents[px] = py;
node_counter--;
sig = 1;
}
if(node_counter)
{
if(sig == 0)
return -1;
else
return -2;
}
return 0;
}
int main()
{
while(scanf("%d%d",&R,&N),R)
{
if(!N)
break;
num_edge = 0,sums = 0;
memset(edges,0,sizeof(edges));
for(int ei = 0,fir,sec,wei;ei < R;++ei)
{
scanf("%d%d%d",&fir,&sec,&wei);
edges[num_edge].source = fir;
edges[num_edge].target = sec;
edges[num_edge].weight = wei;
num_edge++;
}
node_counter = N;
sort(edges,edges+R,cmp);
for(int i = 1;i <= N;++i)
{
parents[i] = i;
}
//int signal = 1;
for(int i = 0;i < R;++i)
{
int sig = merge(edges[i].source,edges[i].target);
if(sig == 0)
{
break;
}
else if(sig == -2)
sums += edges[i].weight;
}
if(node_counter == 1)
printf("%d\n",sums);
else
printf("?\n");
}
}