week12

T1
题目描述

给出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

求解思路

初始化数组,因为已知数据范围,可以直接将数字对应数组下标,数组中存储该下标数字出现的次数,当次数大于等于(n+1)/2时,输出该数字。

代码
#include<iostream>
#include<stdio.h>
using namespace std;

#define maxx 1000005
int n;
int arr[1000005];
int main()
{
	while (scanf("%d", &n) != EOF)
	{
		for (int i = 1; i <= maxx; i++)
		{
			arr[i] = 0;
		}
		for (int i = 1; i <= n; i++)
		{
			int tmp;
			cin >> tmp;
			arr[tmp]++;
			if (arr[tmp] == (n + 1) / 2)
			{
				cout << tmp << endl;
			}
		}
	}
}
T2
题目描述

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!

求解思路

通过三维数组存储空间位置,采用bfs的方法从起点开始,向外扩展,取出队列头元素,判断当前元素的上下左右前后六个位置,若该位置没有到达过,则加入队列,循环该过程至队列为空,同时,每次找到没有到达过的位置时,记录该位置据起点的距离,即为父元素距离起点距离+1,若找到E,则输出Escaped in x minute(s),返回0,若队列为空,返回1表示没有找到,输出Trapped!
其思想类似于二维空间的迷宫问题。

代码
#include<iostream>
#include<stdio.h>
#include<queue>
using namespace std;

char arr[31][31][31];
int state[31][31][31];
int L, R, C;

struct point
{
	int sl, sr, sc;
};
point s;

int pl[] = { 1,-1,0,0,0,0 };
int pr[] = { 0,0,-1,1,0,0 };
int pc[] = { 0,0,0,0,-1,1 };
int solve()
{
	queue<point> que;
	state[s.sl][ s.sr][ s.sc] = 0;
	que.push(s);
	while (!que.empty())
	{
		point fa = que.front();
		point tmp = que.front();
		que.pop();
		
		for (int i = 0; i < 6; i++)
		{
			tmp.sl =fa.sl+pl[i];
			tmp.sr = fa.sr + pr[i];
			tmp.sc = fa.sc + pc[i];
			if (tmp.sl >= 0 && tmp.sl < L && tmp.sr >= 0 && tmp.sr < R && tmp.sc >= 0 && tmp.sc < C)
			{
				if (arr[tmp.sl][tmp.sr][tmp.sc] == 'E')
				{
					state[tmp.sl][tmp.sr][tmp.sc] = state[fa.sl][fa.sr][fa.sc] + 1;
					cout << "Escaped in " << state[tmp.sl][tmp.sr][tmp.sc] << " minute(s)." << endl;
					return 0;
				}
				if (arr[tmp.sl][tmp.sr][tmp.sc] == '.' && state[tmp.sl][tmp.sr][tmp.sc] == -1)
				{
					//cout << tmp.sl << tmp.sr << tmp.sc << endl;
					state[tmp.sl][tmp.sr][tmp.sc] = state[fa.sl][fa.sr][fa.sc] + 1;
					que.push(tmp);
					
				}
			}
		}
		
		
	}
	return 1;
}
int main()
{
	while (1)
	{
		cin >> L >> R >> C;
		if (L == 0 && R == 0 && C == 0)
		{
			break;
		}
		for(int i=0;i<L;i++)
			for(int j=0;j<R;j++)
				for (int k = 0; k < C; k++)
				{
					state[i][j][k] = -1;
					char tmp;
					cin >> tmp;
				//	cout << tmp << endl;
					arr[i][j][k] = tmp;
					if (tmp == 'S')
					{
						s.sl = i;
						s.sr = j;
						s.sc = k;
					}
				}
	//	cout << "aa";
		int con = solve();
		if (con == 1)
		{
			cout << "Trapped!"<< endl;
		}
	}
}
T3
题目描述

东东每个学期都会去寝室接受扫楼的任务,并清点每个寝室的人数。
每个寝室里面有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处理。

求解思路

如代码所示,动态规划求解时,k记录当前寝室数下的进行m次扫楼的结果,由!k更新而来,j表示进行段数,最大为m,最后一维记录当前段选与不选。
当段数小于寝室数时,动态规划方程为:
dp[k][j][1] = max(dp[!k][j][1] + value , max(dp[!k][j - 1][0] + value, dp[!k][j - 1][1] + value));
dp[k][j][0] = max(dp[!k][j][0], dp[!k][j][1]);
最终结果为max(dp[k][m][0], dp[k][m][1])

另外,初始化数组应该为负值,因为可能有负数,以及 初始条件 dp[0][0][0]=0;dp[0][0][1]=0; dp[1][0][0]=0;dp[1][0][1]=0;

代码
#include<iostream>
#include<stdio.h>
#include<queue>
#include<algorithm>
using namespace std;

long long dp[2][100005][2];
long long n, m;
int main()
{
//	while (scanf("%lld",&m)!=EOF)
    while (scanf("%lld%lld",&m,&n)!=EOF)
	{
	//	scanf("%lld", &n);
		long long k = 0;//滚动数组
		for(int i=0;i<2;i++)
			for(int j=0;j<=m;j++)
				for(int p=0;p<2;p++)
				{
					dp[i][j][p] = -1e9;
				}
		dp[0][0][0]=0;dp[0][0][1]=0;	dp[1][0][0]=0;dp[1][0][1]=0;
		for (long long i = 1; i <= n; i++)
		{
			long long value;
			scanf("%lld",&value);
		//    cin>>value;  
		    k = !k;
			for (long long j = 1; j <= m; j++)
			{
				if (j > i) break;
				dp[k][j][1] = max(dp[!k][j][1] + value , max(dp[!k][j - 1][0] + value, dp[!k][j - 1][1] + value));
				dp[k][j][0] = max(dp[!k][j][0], dp[!k][j][1]);
			//	cout << dp[k][j][1] << endl;
			}	
		}
		printf("%lld\n",max(dp[k][m][0], dp[k][m][1]));
	}
}
T4
题目描述

We give the following inductive definition of a “regular brackets” sequence:
the empty sequence is a regular brackets sequence,
if s is a regular brackets sequence, then (s) and [s] are regular brackets sequences, and
if a and b are regular brackets sequences, then ab is a regular brackets sequence.
no other sequence is a regular brackets sequence
For instance, all of the following character sequences are regular brackets sequences:
(), [], (()), ()[], ()[()]
while the following character sequences are not:
(, ], )(, ([)], ([(]
Given a brackets sequence of characters a1a2 … an, your goal is to find the length of the longest regular brackets sequence that is a subsequence of s. That is, you wish to find the largest m such that for indices i1, i2, …, im where 1 ≤ i1 < i2 < … < im ≤ n, ai1ai2 … aim is a regular brackets sequence.
Given the initial sequence ([([]])], the longest regular brackets subsequence is [([])].
Input
The input test file will contain multiple test cases. Each input test case consists of a single line containing only the characters (, ), [, and ]; each input test will have length between 1 and 100, inclusive. The end-of-file is marked by a line containing the word “end” and should not be processed.
Output
For each input case, the program should print the length of the longest possible regular brackets subsequence on a single line.
Sample Input
((()))
()()()
([]])
)[)(
([][][)
end
Sample Output
6
6
4
0
6

求解思路

动态规划求解时,i代表左端点,j代表右端点,dp[i][j]表示[i,j]中含有的满足要求的括号数量。
动态规划方程为:
首先dp[i][j]至少含有其子序列含有的最大括号数量

 for (int k = i; k < j; k++)
{
	dp[i][j] =max(dp[i][j], dp[i][k]+dp[k + 1][j]);
}

另外,判断当前序列左右端点是否构成一对括号,若是,比较[i+1,j-1]中的括号数量+2和[i,j]中含有的满足要求的括号数量,取较大值。

if ((str[j] == ')'&&str[i]=='(')|| (str[j] == ']' && str[i] == '['))
{
	dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + 2);
}

为了保证从小序列更新至大序列,所以j从左往右循环递增,i从当前j循环递减至0,这样就保证了每次更新时,用到的其他序列已经是更新过的。

代码
#include<iostream>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string>
using namespace std;


int dp[105][105];
int main()
{
	while (1)
	{
		string str;
		cin >> str;
		if (str == "end")
			return 0;
		for (int i = 0; i < 105; i++)
			for (int j = 0; j < 105; j++)
				dp[i][j] = 0;
		for (int j = 0; j < str.size(); j++)
		{
			for (int i = j-1; i >= 0; i--)
			{
				for (int k = i; k < j; k++)
				{
					dp[i][j] =max(dp[i][j], dp[i][k]+dp[k + 1][j]);
				}
				if ((str[j] == ')'&&str[i]=='(')|| (str[j] == ']' && str[i] == '['))
				{
					dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + 2);
				}
				
			}
		}

		cout << dp[0][str.size() - 1] << endl;
	}
}
T5
题目描述

马上假期就要结束了,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 小,故输出前一种方案

求解思路

C数组记录完成任务需要的天数,D数组记录截止时间。
采用状态压缩dp的方法,f[k]表示当前k选择下扣掉的分数,比如k的二进制为111101,则表示完成1、3、4、5、6作业,未完成2作业时,扣的最小分数。k的范围为0~ (1 << n) - 1,也就是若有4个作业,则k为0~(1111)二进制
动态规划方程:
首先,当前k下任务完成需要的天数计算:
计算每一位,累加

int sum;
sum = 0;
for (int x = 0; x < n; x++) 
{
	if (k & (1 << x))
    {
	sum += C[x];
	}
}

进而,对n个任务进行判断,tmp为当前k下完成p任务所扣掉的分数,比较原有的完成该任务所扣掉的分数与当前情况下完成p任务所扣掉的分数,取较小值,若当前情况下完成p任务所扣掉的分数更小,即发生了更新,则前驱情况记录到pre数组中,更新的任务记到pr数组中。

for (int p = 0; p < n; p++)
{
	int tmp;
	tmp = max(sum + C[p] - D[p], 0);
	if (f[k] + tmp < f[k | (1 << p)])
	{
		f[k | (1 << p)] = f[k] + tmp;
		pre[k | (1 << p)] = k;
		pr[k | (1 << p)] = p;
	}
}

最少扣的分数为f[maxlen],即当n个任务时,maxlen二进制为1……1(n个1)
采用递归的方式输出方案:

void output(int s)
{
	if (pre[s] != -1)
	{
		output(pre[s]);
		cout << name[pr[s]] << endl;
	}
	else
	{
		return;
	}
}
代码
#include<iostream>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string>
#include<math.h>
using namespace std;



int f[40000];
int pre[40000];
int pr[40000];
int C[20];
int D[20];
string name[20];
void output(int s)
{
	if (pre[s] != -1)
	{
		output(pre[s]);
		cout << name[pr[s]] << endl;
	}
	else
	{
		return;
	}
	
}
int main()
{
	int g;
	cin >> g;
	for (int i = 0; i < g; i++)
	{
		int n;
		cin >> n;
		for (int j = 0; j < n; j++)
		{
			name[j].clear();
			cin >> name[j] >> D[j] >> C[j];
		}
		
		int maxlen = (1 << n) - 1;
		//初始化
		for (int k = 0; k < 40000; k++)
		{
			pre[k] = -1;
			pr[k] = 0;
			f[k] = 0xffff;
		}
		
		f[0] = 0;
	
		for (int k = 0; k < maxlen; k++)
		{
			int sum;
			sum = 0;
			for (int x = 0; x < n; x++) 
			{
				if (k & (1 << x))
				{
					sum += C[x];
				}
			}
			for (int p = 0; p < n; p++)
			{
				int tmp;
				tmp = max(sum + C[p] - D[p], 0);
				if (f[k] + tmp < f[k | (1 << p)])
				{
					f[k | (1 << p)] = f[k] + tmp;
					pre[k | (1 << p)] = k;
					pr[k | (1 << p)] = p;
				}
			}
		}
		cout << f[maxlen] << endl;
		output(maxlen);

	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值