程序设计思维与实践 Week12 作业

A - 必做题 - 1

给出n个数,zjm想找出出现至少(n+1)/2次的数, 现在需要你帮忙找出这个数是多少?

Input
本题包含多组数据:
每组数据包含两行。
第一行一个数字N(1<=N<=999999) ,保证N为奇数。
第二行为N个用空格隔开的整数。
数据以EOF结束。

Output
对于每一组数据,你需要输出你找到的唯一的数。

Sample Input
5
1 3 2 3 3
11
1 1 1 1 1 5 5 5 5 5 5
7
1 1 1 1 1 1 1

Sample Output
3
5
1


思路:用map记录次数,注意要把每次的数读完,不要得到结果后立刻break;


#include <iostream>
#include <map>
using namespace std;

int main()
{
	int n; 
	while (cin >> n)
	{
		map<int, int> m;
		bool flag = false;
		for (int i = 0; i < n; i++)
		{
			int temp; cin >> temp;
			if (!flag && ++m[temp] == (n + 1) / 2)
			{
				cout << temp << '\n';
				flag = true;
			}
		}
	}
}

B - 必做题 - 2

zjm被困在一个三维的空间中,现在要寻找最短路径逃生!
空间由立方体单位构成。
zjm每次向上下前后左右移动一个单位需要一分钟,且zjm不能对角线移动。
空间的四周封闭。zjm的目标是走到空间的出口。
是否存在逃出生天的可能性?如果存在,则需要多少时间?

Input
输入第一行是一个数表示空间的数量。
每个空间的描述的第一行为L,R和C(皆不超过30)。
L表示空间的高度,R和C分别表示每层空间的行与列的大小。
随后L层,每层R行,每行C个字符。
每个字符表示空间的一个单元。’#‘表示不可通过单元,’.‘表示空白单元。
zjm的起始位置在’S’,出口为’E’。每层空间后都有一个空行。
L,R和C均为0时输入结束。

Output
每个空间对应一行输出。
如果可以逃生,则输出如下
Escaped in x minute(s).
x为最短脱离时间。

如果无法逃生,则输出如下
Trapped!

Sample Input
3 4 5
S….
.###.
.##…
###.#

##.##
##…

#.###
####E

1 3 3
S##
#E#

0 0 0

Sample Output
Escaped in 11 minute(s).
Trapped!


思路:一个三维的dfs。


#include <iostream>
#include <queue>
using namespace std;
const int MAXL = 30 + 5;
const int MAXN = MAXL * MAXL * MAXL;
int a[MAXL][MAXL][MAXL], ans, L, R, C;//0不可通过,1可通过,2S,3E
bool vis[MAXL][MAXL][MAXL];
struct position
{
	int l, r, c, length;
	position(int theL, int theR, int theC, int theLength) 
		:l(theL), r(theR), c(theC), length(theLength) {}
};

int dl[6] = { -1,1,0,0,0,0 };
int dr[6] = { 0,0,-1,1,0,0 };
int dc[6] = { 0,0,0,0,-1,1 };

void init()
{
	for (int l = 0; l < L; l++)
		for (int r = 0; r < R; r++)
			for (int c = 0; c < C; c++)
				vis[l][r][c] = false;
}
bool bfs(int SL, int SR, int SC, int EL, int ER, int EC)
{
	queue<position> q;
	q.push(position(SL, SR, SC, 0));
	vis[SL][SR][SC] = true;
	while (q.size())
	{
		position p = q.front(); q.pop();
		if (p.l == EL && p.r == ER && p.c == EC)
		{
			ans = p.length;
			return true;
		}
		for (int i = 0; i < 6; i++)
		{
			position nxtP(p.l + dl[i], p.r + dr[i], p.c + dc[i], p.length + 1);
			if (nxtP.l >= 0 && nxtP.l <= L - 1 && nxtP.r >= 0 && nxtP.r <= R - 1
				&& nxtP.c >= 0 && nxtP.c <= C - 1 && !vis[nxtP.l][nxtP.r][nxtP.c]
				&& a[nxtP.l][nxtP.r][nxtP.c]) q.push(nxtP), vis[nxtP.l][nxtP.r][nxtP.c] = true;
		}
	}
	return false;
}
int main()
{
	while (cin >> L >> R >> C)
	{
		init();
		if (L == 0 && R == 0 && C == 0) return 0;
		int SL, SR, SC, EL, ER, EC;
		for (int l = 0; l < L; l++)
			for (int r = 0; r < R; r++)
				for (int c = 0; c < C; c++)
				{
					char temp; cin >> temp;
					if (temp == '.') a[l][r][c] = 1;
					else if (temp == '#') a[l][r][c] = 0;
					else if (temp == 'S')
					{
						a[l][r][c] = 2;
						SL = l, SR = r, SC = c;
					}
					else if (temp == 'E')
					{
						a[l][r][c] = 3;
						EL = l, ER = r, EC = c;
					}
				}
		if (bfs(SL, SR, SC, EL, ER, EC))
			cout << "Escaped in " << ans << " minute(s)." << endl;
		else cout << "Trapped!" << endl;
	}
}

C - 必做题 - 3

东东每个学期都会去寝室接受扫楼的任务,并清点每个寝室的人数。
每个寝室里面有ai个人(1<=i<=n)。从第i到第j个宿舍一共有sum(i,j)=a[i]+…+a[j]个人
这让宿管阿姨非常开心,并且让东东扫楼m次,每一次数第i到第j个宿舍sum(i,j)
问题是要找到sum(i1, j1) + … + sum(im,jm)的最大值。且ix <= iy <=jx和ix <= jy <=jx的情况是不被允许的。也就是说m段都不能相交。
注:1 ≤ i ≤ n ≤ 1e6 , -32768 ≤ ai ≤ 32767 人数可以为负数。。。。(1<=n<=1000000)
Input
输入m,输入n。后面跟着输入n个ai
Output
输出最大和
Sample Input
1 3 1 2 3
2 6 -1 4 -2 3 -2 3
Sample Output
6
8
Hint
数据量很大,需要scanf读入和dp处理。


思路:动态规划,dp[i][j]代表最后选的一个数是a[j],且已经选了i段,转移方程:
dp[i][j]=max(dp[i][j - 1] + 1, dp[i -1][k])//k < j
可以省略一维,记dp[i]为最后一个数选a[i]且已经选了x段,用dp2[i]记录已经选了x-1段且最后一个数小于等于i的最大值。


#include <iostream>
#include <algorithm>
#define inf 1e9;
using namespace std;
const int MAXN = 1e6 + 5;
int m, n, a[MAXN], dp[MAXN], dp2[MAXN];

int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	while (cin >> m >> n)
	{
		for (int i = 1; i <= n; i++) cin >> a[i];
		memset(dp2, 0, sizeof(int) * MAXN);
		dp[0] = 0;
		int ans;
		for (int i = 1; i <= m; i++)
		{
			ans = -inf;
			for (int j = i; j <= n; j++)
			{
				dp[j] = max(dp[j - 1] + a[j], dp2[j - 1] + a[j]);
				dp2[j - 1] = ans;
				ans = max(ans, dp[j]);
			}
		}
		cout << ans << endl;
	}
}

D - 选做题 - 1

大意是给出括号,找出最长子序列,他是合法括号,比如(), [], (()), ()[], ()[()]
Sample Input
((()))
()()()
([]])
)[)(
([][][)
end
Sample Output
6
6
4
0
6


思路:dp[i][j]记录[i,j]区间的最大长度,转移方程:

dp[i][j]=max(2+dp[i+1][j-1]/*str[i]==str[j]*/,dp[i][k]+dp[k][j]);

转移的思想是这样:对于[i,j]他比[i+1,j-1]多了两边两个,如果他们相等,那么可能就是dp[i+1][j-1]+2。对于更普遍的情况,左边的可能是(,他可能与某个)匹配形成最好的答案,那么就枚举断点,选出最小的。


#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

bool match(char x, char y)
{
	return (x == '(' && y == ')') || (x == '[' && y == ']');
}
const int MAXN = 1e2 + 5;
int dp[MAXN][MAXN];
int main()
{
	string str;
	while (getline(cin, str))
	{
		if (str == "end") return 0;
		int n = str.size();
		for (int i = 0; i < n; i++) dp[i][i] = dp[i + 1][i] = 0;
		for (int i = n - 2; i >= 0; i--)
			for (int j = i + 1; j < n; j++)
			{
				dp[i][j] = 0;
				if (match(str[i], str[j]))
					dp[i][j] = dp[i + 1][j - 1] + 2;
				for (int k = i + 1; k < j; k++)
					dp[i][j] = max(dp[i][j], dp[i][k] + dp[k][j]);
			}
		cout << dp[0][n - 1] << endl;
	}
	return 0;
}


E - 选做题 - 2

Description
马上假期就要结束了,zjm还有 n 个作业,完成某个作业需要一定的时间,而且每个作业有一个截止时间,若超过截止时间,一天就要扣一分。
zjm想知道如何安排做作业,使得扣的分数最少。
Tips: 如果开始做某个作业,就必须把这个作业做完了,才能做下一个作业。
Input
有多组测试数据。第一行一个整数表示测试数据的组数
第一行一个整数 n(1<=n<=15)
接下来n行,每行一个字符串(长度不超过100) S 表示任务的名称和两个整数 D 和 C,分别表示任务的截止时间和完成任务需要的天数。
这 n 个任务是按照字符串的字典序从小到大给出。
Output
每组测试数据,输出最少扣的分数,并输出完成作业的方案,如果有多个方案,输出字典序最小的一个。
Sample Input
2
3
Computer 3 3
English 20 1
Math 3 2
3
Computer 3 3
English 6 3
Math 6 3
Sample Output
2
Computer
Math
English
3
Computer
English
Math

Hint
在第二个样例中,按照 Computer->English->Math 和 Computer->Math->English 的顺序完成作业,所扣的分数都是 3,由于 English 的字典序比 Math 小,故输出前一种方案。


思路:规定状态S代表完成作业的情况,S2可以有更小的S1完成某个作业的来,显然是个动态规划,状态S高达15个,适合用状压。

dp[s] = max(dp[s] , dp[x] + max(sum[x] + w[i].last - w[i].ddl, 0));

从小的状态开始推进,考虑他是从少些任意个作业的状态得来的。在检查当前状态完成了作业x,记录下sum[s]+=x的时间。当更新dp时,记录下当前是哪项作业,记录在pre[]中。
输出通过pre递归输出。因为优先选了字典序小的,输出也按字典序。


#include <iostream>
#include <algorithm>
#include <string>
#define inf 1e9
using namespace std;
const int MAXN = 20;
struct work
{
	string name;
	int ddl, last;
}w[MAXN];

int dp[1 << MAXN], sum[1 << MAXN], pre[1 << MAXN];
void output(int x)
{
	if (x == 0) return;
	output(x ^ (1 << pre[x]));
	cout << w[pre[x]].name << endl;
}
int main()
{
	int T; cin >> T;
	while (T--)
	{
		int n; cin >> n;
		for (size_t i = 0; i < n; i++)
			cin >> w[i].name >> w[i].ddl >> w[i].last;
		memset(dp, 0, sizeof(dp));
		memset(sum, 0, sizeof(sum));
		for (int s = 1; s < (1 << n); s++)
		{
			dp[s] = inf;
			for (int i = 0; i < n; i++)
			{
				if (s & (1 << i))
				{
					sum[s] += w[i].last;
					int x = s ^ (1 << i);
					if (dp[s] >= dp[x] + max(sum[x] + w[i].last - w[i].ddl, 0))
					{
						dp[s] = dp[x] + max(sum[x] + w[i].last - w[i].ddl, 0);
						pre[s] = i;
					}
				}
			}
		}
		cout << dp[(1 << n) - 1] << endl;
		output((1 << n) - 1);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值