程序设计思维与实践第九十周实验小结

第九周
A题 目录管理器
实现一个目录管理器,包括以下操作:
MKDIR s 在当前目录下创建一个子目录 s,s 是一个字符串 创建成功输出 “OK”;若当前目录下已有该子目录则输出 “ERR”。
RM s 在当前目录下删除子目录 s,s 是一个字符串 删除成功输出 “OK”;若当前目录下该子目录不存在则输出 “ERR”。
CD s 进入一个子目录 s,s 是一个字符串 进入成功输出 “OK”;若当前目录下该子目录不存在则输出 “ERR” 特殊地,若 s 等于 “…” 则表示返回上级目录,返回成功输出 “OK”,返回失败则输出 “ERR”
SZ 输出当前目录的大小 也即输出 1+当前目录的子目录数
LS 输出多行表示当前目录的 “直接子目录” 名 若没有子目录,则输出 “EMPTY”;若子目录数属于 [1,10] 则全部输出;若子目录数大于 10,则输出前 5 个,再输出一行 “…”,输出后 5 个。
TREE 输出多行表示以当前目录为根的子树的前序遍历结果 若没有后代目录,则输出 “EMPTY”;若后代目录数+1属于 [1,10] 则全部输出;若后代目录数+1大于 10,则输出前 5 个,再输出一行 “…”,输出后 5 个。
UNDO 撤销操作 撤销最近一个 “成功执行” 的操作(即MKDIR或RM或CD)的影响,撤销成功输出 “OK” 失败或者没有操作用于撤销则输出 “ERR”
初始时没有文件,当前目录为根目录root,每个目录的儿子必须保证字典序。按照操作进行输出。
在这道题中,有一个非常关键的词:当前目录。每一步操作都需要当前目录的位置,为此,我们设置一个全局变量记录当前目录的位置,当当前目录发生改变时更改记录。
本题看似功能明确,实则不同操作之间相互影响。例如,创建和删除目录都会导致部分目录的子目录数和子树遍历结果发生改变。如果我们在每次“SZ”和“TREE”的操作中都进行查找,虽然可以忽略这种改变,但是过多的操作次数与数据会使程序运行时间超出限制;为了避免此情况,我们就要记录每一个目录的具体信息,在由于部分操作导致信息更改时及时更新,这就要求我们在考虑每一步操作的实现时,也要考虑其对其他目录的影响。因此本题看似功能之间界限明显,实则存在内在关联。
MKDIR 创建子目录。首先判断该子目录是否存在,存在输出“ERR”,不存在则在当前目录中插入该子目录,同时对每一个受到影响的目录进行数据更新。由于要保证字典序,对于每一个目录的子目录,我们用map数组存储,按键为输入字符串,值为我们给该目录标记的序号。对于每一个目录,我们用结构体记录其信息,用结构体数组记录所有目录信息(操作合理性为目录数不超过5000个)。为保证“UNDO”操作的实现,我们要将操作名以及处理的目录名作为一条整体信息储存起来,便于“UNDO”操作时将其取出进行操作。
RM 删除子目录。首先判断该目录是否存在,不存在输出“ERR”,存在则在当前目录中删除该子目录,同时对每一个受到影响的目录进行数据更新。删除过程我们只需要将该目录从当前目录的子目录map数组中删除即可(这样既能保证删除,又便于后序的恢复)。为保证“UNDO”操作的实现,我们同样要将操作名以及处理的目录名作为一条整体信息储存起来,便于“UNDO”操作时将其取出进行操作。
CD 进入目录。根据情况,如果需要返回上级目录首先判断上级目录是否存在。不存在输出“ERR”,存在将当前目录更新为原当前目录的父目录,并将操作名以及处理的目录名作为一条整体信息储存起来便于“UNDO”操作。如果需要根据文件名进入子目录,首先判断当前目录下该子目录是否存在,不存在输出“ERR”,存在将当前目录更新为该子目录,并将操作名以及处理的目录名作为一条整体信息储存起来便于“UNDO”操作。
SZ 输出目录大小。根据当前目录位置,输出目录大小。
LS 输出当前目录的直接子目录名。由MKDIR操作我们可以知道,一个目录的所有直接子目录存储在该目录信息的map数组中。我们根据该目录下map数组中的元素个数进行相应格式的输出便实现了LS的操作。
TREE 输出以当前目录为根的子树的前序遍历结果。如果每次操作都进行遍历,运行时间会很长。我们不妨用vector数组对前序遍历结果进行记录。当以该目录为根的子树没有改变时,前序结果不变,我们直接输出结果即可。如果子树改变,那么前序结果可能改变,我们就要重新查找(如何判断子树改变?增减目录——若大小改变则子树一定改变,所以我们在更新目录大小的同时还需要记录该目录已被改变)。若目录大小小于10,输出子树的前序遍历结果,如果大于10,则前序遍历找前五个,后序遍历找后五个,再按照格式输出。
UNDO 撤销操作。我们要执行撤销操作首先就要知道该UNDO前的第一个有效MKDIR/RM/CD操作。在之前的操作中,我们已经将UNDO之前的所有有效操作进行了存储。每次UNDO操作,我们都要取出所有已存储的有效操作中的最后一个。根据其指令对涉及到的目录执行相反操作:MKDIR我们执行删除,RM我们将子目录重新连接,CD我们返回原来的子目录。在操作的同时不忘对数据进行更新处理,若是所有有效操作都被撤销,那么UNDO操作便输出"ERR”表示撤销失败。
本实验的核心思想是记忆化存储。由于不同操作之间相互影响,所有目录的信息都会随着一个操作的改变而发生变化,而在操作次数明显多于目录数,即一个目录注定要被操作多次时,我们单调执行每一个功能显然是不明智的。这时我们就需要运用记忆化存储的思想,在操作时动态存储,并在输出时精准输出。节约不必要的步骤,也使得每一步操作更加整体化。

#include<iostream>
#include<string>
#include<string.h>
#include<map>
#include<vector>

using namespace std;

struct Directory
{   string name;
	map<string, int> mp;
	int father;
	int sz;
	vector<string> tree;
	bool flag;
};

Directory d[10000];

int now,num;

struct Cmd
{   string cmd;
	int first;
	int second;};

vector<Cmd> v;

void update(int id,int num)
{
	while(id!=-1)
	{
		d[id].flag=0;
		d[id].sz=d[id].sz+num;
		id=d[id].father;
	}
}

void MKDIR(string& s)
{
	if(d[now].mp.count(s)==1)
		cout<<"ERR"<<endl;
	else
	{   d[now].mp.insert(pair<string, int>(s,num));
		update(now,1);
		d[num].name=s;
		d[num].father=now;
		d[num].sz=1;
		d[num].flag=0;
		d[num].mp.clear();
		d[num].tree.clear();
		Cmd c;
		c.cmd="MKDIR",c.first=now,c.second=num;
		v.push_back(c);
		num++;
		cout<<"OK"<<endl;
	}
}

void RM(string& s)
{
	if (d[now].mp.count(s)==1)
	{
		int id=d[now].mp[s];
		d[now].mp.erase(s);
		update(now,(-1)*d[id].sz);
		Cmd c;
	    c.cmd="RM",c.first=now,c.second=id;
		v.push_back(c);
		cout<<"OK"<<endl;
	}
	else	cout<<"ERR"<<endl;
}

void CD(string& s)
{
	if (s=="..")
	{
		if (d[now].father==-1)
			cout<<"ERR"<<endl;
		else
		{   Cmd c;
			c.cmd="CD",c.first=now,c.second=d[now].father;
			v.push_back(c);
			now=d[now].father;
			cout<<"OK"<<endl;
		}
	}
	else
	{   if(d[now].mp.count(s)==1)
		{
			Cmd c;
			c.cmd="CD",c.first=now,c.second=d[now].mp[s];
			v.push_back(c);
			now=d[now].mp[s];
			cout<<"OK"<<endl;
		}
		else cout<<"ERR"<<endl;
	}
}

void SZ()
{cout<<d[now].sz<<endl;}

void LS()
{
	if (d[now].mp.size()==0)
		cout<<"EMPTY"<<endl;
	else if(d[now].mp.size()>=1&&d[now].mp.size()<=10)
	{
		for(map<string, int>::iterator it=d[now].mp.begin();it!= d[now].mp.end();it++)
			cout<<it->first<<endl;
	}
	else
	{   map<string, int>::iterator it=d[now].mp.begin();
		for (int i=0;i<5;i++)
		{   cout<<it->first<<endl;
			it++;}
		cout<<"..."<<endl;
		it=d[now].mp.end();
		for(int i=1;i<=5;i++)
			it--;
		for(int i=1;i<=5;i++)
		{
			cout<<it->first<<endl;
			it++;
		}}}

void treeALL(int id,vector<string>& tree)
{
	tree.push_back(d[id].name);
	for(map<string, int>::iterator it=d[id].mp.begin();it!=d[id].mp.end();it++)
		treeALL(it->second,tree);
}

void treePRE(int id,vector<string>& tree,int& num)
{
	tree.push_back(d[id].name);
	num--;
	if(num==0)	return;
	for(map<string, int>::iterator it=d[id].mp.begin();it!=d[id].mp.end();it++)
	{
		treePRE(it->second,tree,num);
		if (num==0)	break;
	}}

void treeBACK(int id,vector<string>& tree,int& num)
{
	map<string, int>::iterator it=d[id].mp.end();
	int n=d[id].mp.size();
	for (int i=0;i<n;i++)
	{
		it--;
		treeBACK(it->second,tree,num);
		if (num==0)	return;
	}
	tree.push_back(d[id].name);
	num--;
}

void pushdown(int id)
{
	d[id].tree.clear();
	if (d[id].sz<=10)
		treeALL(id,d[id].tree);
	else
	{   int num=5;
		treePRE(id,d[id].tree,num);
		num=5;
		treeBACK(id,d[id].tree,num);}
	d[id].flag=1;
}

void TREE()
{
	if (!d[now].flag)
		pushdown(now);
	if (d[now].sz==1)
		cout<<"EMPTY"<<endl;
	else if(d[now].sz>1&&d[now].sz<=10)
	{   for(int i=0;i<d[now].tree.size();i++)
			cout<<d[now].tree[i]<<endl;
	}
	else
	{
		for(int i=0;i<5;i++)
			cout<<d[now].tree[i]<<endl;
		cout<<"..."<<endl;
		for(int i=9; i>=5;i--)
			cout<<d[now].tree[i]<<endl;
	}}

void UNDO()
{
	if(v.size()==0)
		cout<<"ERR"<<endl;
	else
	{
		Cmd c=v[v.size() - 1];
		v.pop_back();
		if(c.cmd=="MKDIR")
		{
			update(c.first,(-1)*d[c.second].sz);
			d[c.first].mp.erase(d[c.second].name);
		}
		else if(c.cmd=="RM")
		{
			update(c.first,d[c.second].sz);
			d[c.first].mp[d[c.second].name]=c.second;
		}
		else
			now=c.first;
		cout<<"OK"<<endl;
	}
}

int main()
{
	int T;
	cin>>T;
	for(int j=0;j<T;j++)
	{   int Q;
		cin>>Q;
		now=0,num=1;
		d[0].name="root",d[0].father=-1,d[0].sz=1,d[0].flag=0;
		d[0].tree.clear(),d[0].mp.clear();
		v.clear();
		string s;
		for(int k=0;k<Q;k++)
		{
			cin>>s;
			if(s=="MKDIR")
			{   cin>>s;
				MKDIR(s);}
			else if (s=="RM")
			{   cin>>s;
				RM(s);}
			else if(s=="CD")
			{   cin>>s;
				CD(s);
			}
			else if(s=="SZ")
				SZ();
			else if(s=="LS")
				LS();
			else if(s=="TREE")
				TREE();
			else if(s=="UNDO")
				UNDO();
		}
		cout<<endl;
	}
}

B题
忽略扑克牌颜色,由五张牌组成一手牌。根据相应规则比较“一手牌“大小,并按由大到小的顺序输出排名(大小相等则名字字典序较小者排名靠前)。
由于“一手牌”的大小不仅与牌的大小有关,还与每种牌的数量有关。所以我们要记录“一手牌”中牌的数量与大小,由此确定种类。根据规则,在牌型相等时,我们至多还需要再比较三类值(即若要比较一手牌的大小,我们至多比较四个数值)。所以对于每一种一手牌,我们除了要记录其主人,牌的具体情况,还要定义四个变量储存这四个数值。我们在得到所有一手牌后,对每一个一手牌进行处理,给予其对应大小的数值。然后按照规则对一手牌进行排序和输出便得到了最终结果。
给自己的忠告:请注意细节……

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<string>
using namespace std;
struct P{
	int value;
	int num;
	bool operator<(const P &p) const{
	if(num!=p.num) return num<p.num;
	else       return value<p.value;}
};
void dicla(int *a,int &cla,int &num1,int &num2,int &num3){
    	P s[14];
    	for(int i=0;i<14;i++){
    		s[i].num=0;
    		s[i].value=0;
		}
		num1=0;num2=0;num3=0;cla=0;
    	for(int i=0;i<5;i++){s[a[i]].value=a[i];s[a[i]].num++;}
		sort(s,s+14);
		if(s[13].num==3){
			if(s[12].num==2){
				cla=5;
				num1=s[13].value;
				num2=s[12].value;}
			else {
				cla=4;
				num1=s[13].value;
				num2=s[12].value+s[11].value;
			}
		}
		else if(s[13].num==2){
			if(s[12].num==2){
				cla=3;
				num1=s[13].value;
				num2=s[12].value;
				num3=s[11].value;}
			else {
				cla=2;
				num1=s[13].value;
				num2=s[12].value+s[11].value+s[10].value;
			}
		}
		else if(s[13].num==4){
			    cla=6;
			    num1=s[13].value;
			    num2=s[12].value;}
			    
		else {
			if(a[0]==1&&a[1]==10){
				int l=1;
				for(int i=1;i<5;i++){
					if(a[i]!=i+9){
						l=0;
					}
				}
				if(l==1) cla=8;
				else     cla=1;
			}
			else{
			int pan=1;
			for(int i=1;i<5;i++){
				if(a[i]-a[i-1]!=1){
					pan=0;}}
			if(pan==1){cla=7;num1=a[4];}
			else {cla=1;for(int i=0;i<5;i++){
				num1=num1+a[i];
			}}}	
		}
		return;}	
struct player{
	char name[10];
	int poke[5];
	int cla;
	int num1;
	int num2;
	int num3;
	bool operator<(const player &p) const{
	if(cla!=p.cla) return cla<p.cla;
	if(num1!=p.num1) return num1<p.num1;
	if(num2!=p.num2) return num2<p.num2;
	if(num3!=p.num3) return num3<p.num3;
	else{     for(int i=0;i<10;i++){
		if(name[i]!=p.name[i]) return name[i]>p.name[i]; 
	}}}
};
int main(){ 
    int n;
    int t=0,j=0;
    while(scanf("%d",&n)!=EOF){
    	player p[n];
    	char s[10];
    	for(int i=0;i<n;i++){
    		scanf("%s",&p[i].name);
    		scanf("%s",&s);
    		t=0;j=0;
    		while(t<5&&j<10){
    			if(s[j]>'1'&&s[j]<='9'){p[i].poke[t]=s[j]-48;j++;t++;}
    			else if(s[j]=='A') {p[i].poke[t]=1;j++;t++;}
    			else if(s[j]=='J') {p[i].poke[t]=11;j++;t++;}
    			else if(s[j]=='Q') {p[i].poke[t]=12;j++;t++;}
    			else if(s[j]=='K') {p[i].poke[t]=13;j++;t++;}
    			else if(s[j]=='1'&&s[j+1]=='0'){p[i].poke[t]=10;j=j+2;t++;}
			}
			}
		for(int i=0;i<n;i++){
			sort(p[i].poke,p[i].poke+5);
			dicla(p[i].poke,p[i].cla,p[i].num1,p[i].num2,p[i].num3);
			}
		sort(p,p+n);
		for(int i=n-1;i>=0;i--){
		     printf("%s\n",p[i].name);
		}
	}}

C题
给定 x 条长凳。第 i 个长凳上坐着 a_i 个人。这时候又有 y 个人将来到公园,输出所有椅子上的人数的最大值的最大值和最小值。
最大值:y个人全部坐在原来人数最多的长凳上。
最小值:在保证原来人数最多的长凳人数依然最多的同时,让坐在该长凳上的人最少(如果y足够小,小于每条长凳与最多人数长凳的人数差之和,我们就可以保证y个人坐下后,最多人数依然是原来的最多人数,即为此时的最小值;反之,如果y大于每条长凳与最多人数长凳的人数差之和,在分配座位的过程中,就会出现所有长凳人数一样的情况,在这种情况下新增一个人,我们优先考虑原最多人数长凳。这样,在第一次人数一样的情况出现后,我们还可以排y与差值和之差除上长凳数次。如果余数为0,则刚好排完,反之,我们还要再给该长凳分配一人,在保证每一条长凳人数尽量平均的情况下,我们就得到了最小的最大值)。

#include<iostream>

using namespace std;

int main(){
	int x,y;
	cin>>x>>y;
	int a[x];
	int max=0;
	 
	for(int i=0;i<x;i++){
		cin>>a[i];
		if(a[i]>max){max=a[i];}	}
	int sum=0;
	for(int i=0;i<x;i++){
		sum=sum+max-a[i];
	}
	int mx=0,mn=0;
	mx=max+y;
	if(sum>=y){mn=max;}
	else {y=y-sum;if(y%x==0){mn=max+y/x;}
	                else {mn=max+y/x+1;}}
	cout<<mn<<" "<<mx<<endl; 
} 

第十周 限时大模拟
A题 给A×B×C个小正方体涂上红蓝两色。令所有红色的小正方体组成一个长方体,
所有蓝色的小正方体组成一个长方体,找到红色正方体的数量与蓝色正方体的数量差值的绝对值的最小值。
如果A、B、C中存在偶数,则我们可以通过分割数值为偶数的那条边将大长方体分割为完全相等的两部分,此时差值为0;如果A、B、C都是奇数,那么我们无论从哪一条边进行分割,在这条边分割后两部分在其中一个数值上会相差一,总体相差剩余两个数的乘积。例如,如果ABC都是奇数,我们以A边进行分割,最后差值即为BC。为使差值绝对值最小,我们就要让“BC”最小,即从长度最大的边进行分割,最小值为两个较小数字的乘积。

#include<iostream>
 
using namespace std;

int main(){
	long long int A,B,C;
	long long int sum;
	cin>>A>>B>>C;
	if(A%2==0||B%2==0||C%2==0){cout<<0<<endl;}
	else {
		if(A>=B&&A>=C){
			sum=B*C;
			cout<<sum<<endl;
		}
		else if(B>=A&&B>=C){
			sum=A*C;
			cout<<sum<<endl;
		}
		else if(C>=A&&C>=B){
			sum=A*B;
			cout<<sum<<endl;
		}
	} 
}

B题 团队聚会
给出每位助教的各项事情的时间表,规定可以用来开会的时间段:在该时间段的任何时间点,都应该有至少两人在场。在该时间段的任何时间点,至多有一位成员缺席。该时间段的时间长度至少应该1h。找出所有时间段。
如何判断一个人在某时间段是否缺席?如果他在该时间段内有安排即为缺席。也就是说,一个人会在开始自己的任务时缺席,在结束自己的任务后回到会议,即每个人参加和离开会议的时间就是任务的起始和终止时刻。对每一个时刻,我们计算此时缺席会议的人和参加会议的人。如果满足条件,我们将该时刻视为一个可能存在的会议起始时间,反之,我们将该时刻视为会议终止时间,此时的起始时间为距离终止时间最远的起始时间。我们判断两者之间时间长度是否超过1h,超过则将其作为一个可以用来开会的时间段,反之舍弃。

#include <iostream>
#include <iomanip>
#include <cstring>
#include <vector>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<string>

using namespace std;

struct Time {
    int year,month,day;
    int hour,minute,second;
    int type,people;
    Time() {}

    Time(int a,int b,int c,int d,int e,int f,int g,int h) {
        year=a;
        month=b;
        day=c;
        hour=d;
        minute=e;
        second=f;
		type=g;
		people=h;}

    Time operator=(const Time &b) {
        year=b.year;
        month=b.month;
        day=b.day;
        hour=b.hour;
        minute=b.minute;
        second=b.second;
        type=b.type;
        people=b.people;}

    bool operator<(const Time &b) const {
        if (year!=b.year)return year<b.year;
        if (month!=b.month)return month<b.month;
        if (day!=b.day)return day<b.day;
        if (hour!=b.hour)return hour<b.hour;
        if (minute!=b.minute)return minute<b.minute;
        if (second!=b.second)return second<b.second;
        return type>b.type;
    }

    void Output() {
        cout<<setw(2)<<setfill('0')<<month<< '/';
        cout<<setw(2)<<setfill('0')<<day<< '/';
        cout<<setw(4)<<setfill('0')<<year<< ' ';
        cout<<setw(2)<<setfill('0')<<hour<< ':';
        cout<<setw(2)<<setfill('0')<<minute<< ':';
        cout<<setw(2)<<setfill('0')<<second;

    }
};

vector<Time> s;
vector<pair<Time,Time> > r;
int q[30];

void clr() {
    s.clear();
    r.clear();
}

bool s_time(Time a,Time b){
	long long int t;
	t=a.year-b.year;
	t=t*12+a.month-b.month;
	t=t*30+a.day-b.day;
	t=t*24+a.hour-b.hour;
	t=t*60+a.minute-b.minute;
	t=t*60+a.second-b.second;
	if(t>=3600) return true;
	else        return false;
} 

int main() {
    Time Begin(1800,1,1,0,0,0,-1,-1), End(2200,1,1,0,0,0,-2,-1);
    int T;
    cin>>T;
    int m;
    for(int L=1;L<=T;L++) {
        cin>>m;
        clr();
        s.push_back(Begin);
        s.push_back(End);
        for(int i=0;i<m;++i) {
            int n;
            cin>>n;
            for(int j=0;j<n;++j) {
            	int a,b,c,d,e,f;
                cin>>a>>b>>c>>d>>e>>f;
                Time t1(a,b,c,d,e,f,0,i);
                s.push_back(t1);
                cin>>a>>b>>c>>d>>e>>f;
                Time t2(a,b,c,d,e,f,1,i);
                s.push_back(t2);
                string str; 
                getline(cin,str);}}
        sort(s.begin(),s.begin()+s.size());
        Time b(2300,1,1,0,0,0,-1,-1),b1(2300,1,1,0,0,0,-1,-1), e(100,1,1,0,0,0,-1,-1),e1(100,1,1,0,0,0,-1,-1);
		int p=0;
		for(int i=0;i<m;i++){q[i]=0;}
		int time1=1,time2=0;
		for(int j=0;j<s.size();j++){
			if(s[j].type==0){ 
				q[s[j].people]++;
				if(q[s[j].people]==1) p++;}
			else if(s[j].type==1){
				q[s[j].people]--;
				if(q[s[j].people]==0) p--;}
			else if(s[j].type==-2){p=100;}
			if(p<2&&m-p>1){if(s[j]<b){b=s[j];}}
			else{
				if(e<s[j]){e=s[j];}
				if(s_time(e,b)){r.push_back(make_pair(b,e));}
				b=b1;
				e=e1;}}
		cout<<"Scenario #"<<L<< ":\n";
        if (r.size()==0)cout << "no appointment possible\n";
        else {
            for (int i=0;i<r.size();++i) {
                cout<<"appointment possible from ";
                r[i].first.Output();
                cout<<" to ";
                r[i].second.Output();
                cout<<'\n';
            }
        }
        cout << endl;
    }
    return 0;
}

A题
给定数字n和m,每一步操作中,可以将n乘以2或乘以3,可以进行任意次操作。输出将n转换成m的操作次数,如果转换不了输出-1。
如果是简单的从n乘到m,或者从m除到n,由于我们不知道操作几次,每一次如何操作,当m较大且n较小时,情况会非常复杂。我们不妨这样考虑:每一次n乘以2或乘以3,那么如果可以变为m,证明 m/n 可以整除,且商是多个2和多个3相乘的乘积。我们只要判断m和n相除是否整除、商是否可以被2和3整除即可。由于2和3都是质数,我们可以对商先除3,整除不了再除2,每一步除法都视为操作过程中的一次乘法。如果最后整除结果为1,证明可以整除,反之则不行。

#include<iostream>

using namespace std;

int main(){
	int m,n;
	cin>>n>>m;
	int time=0;
	int p;
	if(m%n==0){
		int p=m/n;
	while(p!=1){
		if(p%3==0) {p=p/3;time++;}
		else if(p%2==0){p=p/2;time++;}
		else {p=1;time=-1;}
	}}
	else{time=-1;}
	cout<<time<<endl;
} 

B题 LIS&LCS
存在两个序列A和B。输出序列A的LIS和序列AB的LCS的长度。
由于LIS是单调递增的,所以到某一个位置的LIS的长度为其之前比其小的位置的LIS长度的最大值+1(+1为自身)。整体序列的LIS就是最末位置的LIS
LCS是公共最长子序列,我们定义:d[i][j]为序列A取到第i个位置,序列B取到第j个位置时的LCS长度。当A[i]与B[j]相等时,该元素便是LCS的一部分,此时d[i][j]=d[i-1][j-1]+1,当不相等时,d[i][j]就是d[i][j-1]和d[i-1][j]中的较大值。AB的LCS即为AB均取到最后位置时的d[i][j]。

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

int a[5050];
int b[5050];
int c[5050];
int d[5050][5050]; 

int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){scanf("%d",&a[i]);c[i]=0;}
    for(int i=1;i<=m;i++) scanf("%d",&b[i]);
    int p=0;
    for(int i=1;i<=n;i++){
    	for(int j=1;j<i;j++){
    		if(a[i]>a[j]){
    			p=max(p,c[j]);
			}}
		c[i]=p+1;
		p=0;
	}
	d[0][0]=0;
	d[0][1]=0;
	d[1][0]=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			d[i][j]=0;
			if(a[i]==b[j]){
				d[i][j]=d[i-1][j-1]+1;
			}
			else{
				d[i][j]=max(d[i-1][j],d[i][j-1]);
			}
		}
		p=max(p,c[i]);
	}
    printf("%d %d\n",p,d[n][m]);
    return 0;
}

C题 拿数问题
给一个序列,里边有 n 个数,每一步能拿走一个数,比如拿第 i 个数, Ai = x,得到相应的分数 x,但拿掉这个 Ai 后,x+1 和 x-1 就会变得不可拿(但是有 Aj = x 的话可以继续拿这个 x)。求最大分数。
这道题注意:是拿了一个数得到相应的分数后,相邻的分数变为不可拿,而非相邻的位置变为不可拿。因而比一般的拿数问题要简单一些:我们记录不同分数及该分数的个数。记录sum[i]为取到第i种分数时的最大值。为方便比较,我们将所有分数按照分值大小进行排列。当我们取到的位置分数与前一个位置分数相差不为1时,该分数可自然计算到最大值中;当相差为1时,我们只能从该位置及其前一个位置中选择一个作为该位置的最大值(此时需要进行比较),最终结果为每一个位置最大值的最大值。

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

int a[100050];
long long int result[100050];
struct st{
    int num;
	long long int value;
};
st s[100050];
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){scanf("%d",&a[i]);}
    sort(a+1,a+n+1);
    int num=0;
    s[num].num=a[1];
    s[num].value=a[1];
    
	for(int i=2;i<=n;i++){
		if(a[i]==s[num].num){
			s[num].value=s[num].value+a[i];
		}
		else{
			num++;
			s[num].num=a[i];
			s[num].value=a[i];
		}}
	result[0]=s[0].value;
	if(s[1].num-s[0].num==1){
	result[1]=max(s[0].value,s[1].value);}
	else{
		result[1]=s[0].value+s[1].value
		;
	}
	for(int i=2;i<=num;i++){
		if(s[i].num-s[i-1].num==1){
			result[i]=max(result[i-1],result[i-2]+s[i].value);
		}
		else result[i]=result[i-1]+s[i].value;
	}
	long long int m=0;
	for(int i=0;i<=num;i++){
		m=max(m,result[i]);
	}
	cout<<m<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值