刷oj碰到的问题3(180916-180917)

(背包问题系列)

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");
    }
}

猜你喜欢

转载自blog.csdn.net/georgeandgeorge/article/details/82722404