前言


此题坑爹啊,描述上有误啊,不玩war3做不出来啊!

比赛时同学说这是道纯模拟,纯模拟我擅长啊,但是这一写就是300行啊,还没写完啊!就算写完也肯定不对啊,因为是按描述理解的啊!

但是这么坑爹的题,也有队伍过啊!一过还他么几十个队啊!OMG啊!坑爹啊!

题目大意


模拟魔兽争霸的装备购买行为。在游戏中,可以购买三种装备:

  • 普通装备:每个普通装备占一个装备栏,可以直接购买。
  • 合成装备:每个合成装备占一个装备栏,购买时需要有足够的材料和合成金。
  • 消耗装备:多个同类型的合成装备占一个装备栏。

另外,还有一些规则上需要注意的地方:

  • 当你的背包满时,你不能购买任何东西。
  • 当你购买合成装备时,相应材料将会从装备栏中消失。
  • 当一个操作非法时,现有装备和金钱不会出现任何变化。
  • 刚开始时,你的金钱为0,没有任何装备。
  • 金钱不能为负数。

思路


纯模拟题。题意稍微有些歧义,不玩war3的同学很难作对这道题。下面说些需要注意的地方:

首先,装备栏满时不可以购买普通装备,这个很正常。对于消耗型装备,装备栏满时,就算已拥有同类型装备,也不能继续购买。但是,合成装备却与以上两种装备不同,在装备栏满时,只要合成金和材料充足,就可以购买!这里就与题目描述相违了。但合成装备有一种特殊情况,就是会出现不需要任何材料的合成装备,这时在规则上可以将其视为普通装备。

因为本人基本没什么war3经验,特地请教了在这个领域里比较拔尖的同学,问其合成装备的材料能否是消耗型装备,得到回答是否,但这个回答让我wa了很久。杭电的测试数据中有用消耗型装备充当材料的情况,并且考虑到了“装备栏满时,消耗型装备数量大于合成需求量导致无法空出装备栏,结果没地方给合成装备”这样的情形。

关于物品价值方面,由于合成装备的材料可以是其他合成装备,因此求取物品价值是一个递归的过程。

代码


#include<iostream>
#include<stdio.h>
#include<map>
#include<string>
using namespace std;
#define NAMESIZE 15
#define EQUIPSIZE 20

int total_room;
//目前拥有的空间 
int total_gold;
//目前拥有的金币数量 
char list_equipment[6][NAMESIZE+1];
//目前拥有的装备的列表 

int total_normal;
//普通装备数量 
struct p1
{
	string name;
	//装备名称 
	int value;
	//装备价值 
}normal[EQUIPSIZE];

int total_mixture;
//混合装备数量 
struct p2
{
	string name;
	//装备名称 
	int value;
	//合成价格 
	int num;
	//合成需要的装备种类的数量 
	string need_name[6];
	//合成需要的装备的种类 
	int need_num[6];
	 //对应合成所需的各种装备的数量 
}mixture[EQUIPSIZE];

int total_consume;
//消耗型装备数量 
struct p3
{
	string name;
	//装备名称 
	int value;
	//装备价值 
	int num;
	//拥有该装备的数量,此参数在init()中被初始化。 
}consume[EQUIPSIZE];

void init()
//初始化 
{
	total_room=0;
	total_gold=0;
}

int check_num(const char temp[])
//检查字符串name[]是否表示一个整数。若是整数,返回其大小,否则返回-1。 
{
	int len=strlen(temp),sum=0;
	for(int i=len-1,j=1;i>=0;i--,j*=10)
		if(temp[i]>='0'&&temp[i]<='9')
			sum+=(temp[i]-'0')*j;
		else
			return -1;
	return sum;
}

pair<int,int> check_exist(const char name[])
//检查装备列表中是否存在装备name,返回值为pair<装备数量,装备列表中的位置。> 
//若是消耗型装备,total==1时表示装备栏中有此装备,但并不表示其数量为1。
//消耗型装备的数量记录在对应类型的结构体中。 
{
	int total=0,pos;
	//total表示该装备在装备列表中的数量。
	//pos表示该装备在装备列表中的位置。  
	for(int i=0;i<total_room;i++)
		if(!strcmp(list_equipment[i],name))
		{
			pos=i;
			total++;
		}
	return pair<int,int>(total,pos);
}

pair<int,int> get_type(const char name[])
//获取装备name的类别信息,返回值为pair<类别,在类别中的位置>
//类别:分别用1、2、3表示普通、混合、消耗型装备。 
{
	for(int i=0;i<total_normal;i++)
		if(!strcmp(normal[i].name.c_str(),name))
			return pair<int,int>(1,i);
	for(int i=0;i<total_mixture;i++)
		if(!strcmp(mixture[i].name.c_str(),name))
			return pair<int,int>(2,i);
	for(int i=0;i<total_consume;i++)
		if(!strcmp(consume[i].name.c_str(),name))
			return pair<int,int>(3,i);
	return pair<int,int>(-1,-1);
	//若找不到,返回pair<-1,-1>。 
}

int get_value(const char name[])
//获取装备name的价值。 
{
	pair<int,int> equip_type_info=get_type(name);
	//equip_type_info装备name的类别信息。 
	if(equip_type_info.first==1)
		return normal[equip_type_info.second].value;
	else if(equip_type_info.first==2)
	//如果是混合装备,需要计算材料成本。 
	{
		p2* equip_info=&mixture[equip_type_info.second];
		//equip_info指向该混合装备的结构体 
		int sum=equip_info->value;
		//sum表示混合装备的价值
		for(int i=0;i<equip_info->num;i++)
		//遍历所有材料的种类 
			sum+=get_value(equip_info->need_name[i].c_str())*(equip_info->need_num[i]);
		return sum;
	}
	else
	{
		p3* equip_info=&consume[equip_type_info.second];
		return equip_info->value*equip_info->num;
	}
}

void delete_equip(const char name[],bool sell=true,int num=-1)
//删除装备列表中的name装备
//参数sell用于判断当前操作是删去装备(合成时删去材料)还是卖掉装备。
//num==-1时,表示要删除一个装备栏中的内容。支持所有类型装备。 
//num>=0时,表示要删除num个消耗型装备。只支持消耗型装备。
//当num>=0时,sell属性无意义,只支持删除装备,不支持卖装备。 
{
	if(num==-1)
	{
		pair<int,int> equip_list_info=check_exist(name);
		if(equip_list_info.first)
		//若拥有该装备(数量不为0) 
		{
			for(int i=equip_list_info.second+1;i<total_room;i++)
			//利用循环将装备从装备列表中删除。 
				strcpy(list_equipment[i-1],list_equipment[i]);
			total_room--;
			if(sell)
			//如果是卖出装备,则需要更新total_gold。 
				total_gold+=get_value(name);
			pair<int,int>equip_type_info=get_type(name);
			if(equip_type_info.first==3)
			//若该装别是消耗型装备,则将其装备数量标记为0。 
				consume[equip_type_info.second].num=0;
		}
	}
	else
	{
		pair<int,int>equip_type_info=get_type(name);
		consume[equip_type_info.second].num-=num;
		if(consume[equip_type_info.second].num==0)
		//当消耗型装备的数量为0时,需要将其从装备列表中删除,采用递归调用方式。 
			delete_equip(consume[equip_type_info.second].name.c_str(),false);
	}
}

void add_equip(const char name[])
//向装备列表中添加装备name 
{
	int add_gold=check_num(name);
	if(add_gold!=-1)
	//若name表示金钱 
	{
		total_gold+=add_gold;
		return;
	}
	pair<int,int> equip_type_info=get_type(name);
	//获取装备name的信息。 
	if(equip_type_info.first==1)
	//若是普通装备
	{
		if(total_room==6)
		//若装备栏已满 
			return;
		int equip_value=normal[equip_type_info.second].value;
		if(total_gold>=equip_value)
		{
			total_gold-=equip_value;
			strcpy(list_equipment[total_room++],name);
		}
	}
	else if(equip_type_info.first==2)
	//若是混合装备 
	{
		p2* equip_info=&mixture[equip_type_info.second];
		if(total_gold>=equip_info->value)
		//若当前金币总数不满足合成价格。 
		{
			int sum=0;
			//sum表示,因增加mixture装备所新增空余装备栏的数量。 
			for(int i=0;i<equip_info->num;i++) 
			//利用循环判断是否有足够的材料。
			{
				const char* temp_name=equip_info->need_name[i].c_str();
				//temp_name表示mixture装备所需的材料名称。 
				if(get_type(temp_name).first!=3)
				//若材料不是消耗型 
					if(check_exist(temp_name).first<equip_info->need_num[i])
						return;
					else
						sum+=equip_info->need_num[i];
				else
				//若材料是消耗型。 
				{
					int temp_num=consume[get_type(temp_name).second].num;
					//temp_num表示消耗型材料的数量。 
					if(temp_num<equip_info->need_num[i])
						return;
					else if(temp_num==equip_info->need_num[i])
					//若消耗型材料的数量与所需数量相等,则可以空出一个装备栏。 
						sum++;
				}
			} 
			if(total_room-sum+1>6)
			//若装备栏的容量符合要求 
				return;
			total_gold-=equip_info->value;
			//更新total_gold 
			for(int i=0;i<equip_info->num;i++)
			//利用循环删除材料。 
			{
				const char* temp_name=equip_info->need_name[i].c_str(); 
				if(get_type(temp_name).first!=3)
					for(int j=0;j<equip_info->need_num[i];j++)
						delete_equip(temp_name,false);
				else
					delete_equip(temp_name,false,equip_info->need_num[i]);
			} 
			strcpy(list_equipment[total_room++],name);
		}
	}
	else if(equip_type_info.first==3) 
	{
		if(total_room==6)
		//若装备栏已满 
			return;
		p3* equip_info=&consume[equip_type_info.second];
		if(total_gold>=equip_info->value)
		{
			total_gold-=equip_info->value;
			equip_info->num++;
			if(!check_exist(name).first)
			//若装备列表中没有同类的消耗装备 
				strcpy(list_equipment[total_room++],name);
		}
	}
	else if(equip_type_info.first==-1)
	//无该装备 
		return;
}

void input_normal_info()
//输入普通装备信息 
{
	char equip_name[NAMESIZE];
	//装备的名称 
	int equip_value;
	//装备的价值 
	for(int i=0;i<total_normal;i++)
	{
		scanf("%s %d",equip_name,&equip_value);
		normal[i].name=equip_name;
		normal[i].value=equip_value;
	}
}

void input_mixture_info()
//输入合成装备信息 
{
	char equip_name[NAMESIZE];
	//装备的名称 
	int consume_value;
	//consume_value表示合成价格 
	scanf("%d",&total_mixture);
	for(int i=0;i<total_mixture;i++)
	{
		scanf("%s %d:",equip_name,&consume_value);
		mixture[i].name=equip_name;
		mixture[i].value=consume_value;
		mixture[i].num=0;
		getchar();
		while(true)
		//下面利用getchar()输入材料名称。这里的处理比较繁琐,相信有更好的解决方案。 
		{
			int need_num;
			//need_num每种装备需要的数量
			bool check_noword=false; 
			//判断当前混合装备是否不需要材料。 
			for(int j=0;;j++)
			{
				equip_name[j]=getchar();
				if(equip_name[0]=='\n')
				{
					check_noword=true;
					break;
				}
				if(equip_name[j]==' ')
				{
					equip_name[j]='\0';
					break;
				}
			}
			if(check_noword)
				break;
			scanf("%d",&need_num);
			mixture[i].need_name[mixture[i].num]=equip_name;
			mixture[i].need_num[mixture[i].num]=need_num;
			mixture[i].num++;
			if(getchar()=='\n')
				break;
			else
				getchar();
		}
	}
}

void input_consume_info()
//输入消耗型装备信息 
{
	char equip_name[NAMESIZE];
	//装备的名称 
	int equip_value;
	//装备的价值 
	scanf("%d",&total_consume);
	for(int i=0;i<total_consume;i++)
	{
		scanf("%s %d",equip_name,&equip_value);
		consume[i].name=equip_name;
		consume[i].value=equip_value;
		consume[i].num=0;
	}
}

void operation()
{
	int m;
	scanf("%d",&m);
	char op[NAMESIZE+2];
	for(int i=0;i<m;i++)
	{
		scanf("%s",op);
		if(op[0]=='+')
			add_equip(op+1);
		else
			delete_equip(op+1);
	}
}

int cmp(const void* m,const void* n)
//将字符串由小到大排序 
{
	return strcmp((char*)m,(char*)n);
}

int Case=1;
void output()
{
	printf("Case %d:\n%d\n%d\n",Case++,total_gold,total_room);
	qsort(list_equipment,total_room,sizeof(list_equipment[0]),cmp);
	//排序 
	for(int i=0;i<total_room;i++)
	{
		pair<int,int> equip_type_info=get_type(list_equipment[i]);
		if(equip_type_info.first==3)
		//如果是消耗型装备,则输出其数量。 
			printf("%s: %d\n",&list_equipment[i],consume[equip_type_info.second].num);
		else
			printf("%s: 1\n",&list_equipment[i]);
	}
	printf("\n");
}

int main()
{
	while(~scanf("%d",&total_normal))
	{
		init();
		input_normal_info();
		input_mixture_info();
		input_consume_info();
		operation();
		output();
	}
}

测试数据


2
ring 1
sword 2
1
knife 3: 
1
medicine 1
8
+100
+ring
+ring
+ring
+ring
+ring
+sword
+knife

2
ring 1
sword 2
1
knife 3: ring 2, sword 2
1
medicine 1
6
+100
+ring
+ring
+sword
+sword
+knife

2
ring 1
sword 2
1
knife 3: ring 2, sword 2
1
medicine 1
5
+100
+ring
+ring
+sword
+knife

2
ring 1
sword 2
2
knife 3: ring 1, sword 1
kkk 3: ring 1, sword 1
1
medicine 1
9
+100
+ring
+ring
+ring
+ring
+sword
+sword
+knife
+kkk

2
ring 1
sword 2
2
knife 3: ring 1, sword 1
kkk 2: knife 2, sword 1
1
medicine 1
9
+100
+ring
+ring
+sword
+sword
+knife
+knife
+sword
+kkk

2
ring 1
sword 2
2
knife 3: ring 1, sword 1
kkk 2: knife 2, sword 1
1
medicine 1
10
+100
+ring
+ring
+sword
+sword
+knife
+knife
+sword
+kkk
-kkk

2
ring 1
sword 2
2
knife 3: ring 1, sword 1
kkk 2: knife 2, medicine 2
1
medicine 1
11
+100
+ring
+ring
+sword
+sword
+knife
+knife
+medicine
+medicine
+medicine
+kkk

2
ring 1
sword 2
2
knife 3: ring 1, sword 1
kkk 2: knife 2, medicine 2
1
medicine 1
12
+100
+ring
+ring
+sword
+sword
+knife
+knife
+medicine
+medicine
+medicine
+kkk
-kkk

2
ring 1
sword 2
2
knife 3: ring 1, sword 1
kkk 2: knife 2, medicine 2
1
medicine 1
10
+100
+ring
+ring
+sword
+sword
+knife
+knife
+medicine
+medicine
+kkk

2
ring 1
sword 2
2
knife 3: ring 1, sword 1
kkk 2: knife 2, medicine 2
1
medicine 1
20
+100
+ring
+ring
+sword
+sword
+knife
+knife
+medicine
+medicine
+kkk
+100
+ring
+ring
+sword
+sword
+knife
+knife
+medicine
+medicine
+kkk

2
ring 1
sword 2
2
knife 3: ring 1, sword 1
kkk 2: knife 2, medicine 2
1
medicine 1
21
+100
+ring
+ring
+sword
+sword
+knife
+knife
+medicine
+medicine
+medicine
+kkk
+100
+ring
+ring
+sword
+sword
+knife
+knife
+medicine
+medicine
+kkk

2
ring 1
sword 2
1
knife 3: medicine 1
1
medicine 1
8
+100
+ring
+ring
+ring
+ring
+ring
+medicine
+knife

2
ring 1
sword 2
1
knife 3: medicine 1
1
medicine 1
9
+100
+ring
+ring
+ring
+ring
+medicine
+medicine
+ring
+knife