PAT-顶级 1021 Safe Fruit (35 分)(可能是目前全网最快的版本)

PAT-顶级 1021 Safe Fruit (35 分)

1. 思路:
起名叫最大团问题,实际上没有这么一个算法。基本上就是暴力搜索加剪枝,dfs的思路。并不需要特别复杂的算法。
遍历节点,依次考察第i个节点加入团所能得到的最大团。
每加入一个节点就更新当前不安全水果集合,加入时考察当前水果是否与篮子中水果相克
(或者按照图的思路,在加入新节点时才考察是否合法,不引入不安全水果集合的概念。)
剪枝思路:
1.不考察重复团,因此要给加入团的节点编序号,按增序排列,因此在考察第i个节点时,只有
【i+1,n】号节点可以加入团。
2.在考察i号节点时,最终将得到【i,n】号节点能形成的最大团中节点数目max_i【i】,那么在考察i-1号节点时就可以利用历史信息。是一个动态规划的思想。
如果当前被考察集合是【i,n】号节点,那么能增加的新节点数就确定了是max_i【i】。
如果最多能得到的数目已经小于当前记录的最大团数目,就无须再考察。

2.减小时间复杂度技巧:
1.缩减序号,由于初始水果序号是三位数,如果考察三位数时间复杂度太高。
根据给定水果数量最多100,可将水果序号与输入顺序形成一一映射关系。减少考察范围。
2.使用二进制位来表示相克关系。用两个longlong型变量的二进制位表示100个水果的相克关系是绰绰有余的。用一个数组保存相克关系,例如F[15]中保存两个longlong型变量,第1个bit为1表示15号水果与1号水果相克,第10个bit为1表示15号水果与10号水果相克。前五十位用一个longlong型数保存,后一个用另一个longlong型数保存,例如第52号水果与15号水果相克,则第二个longlong型数的第2个bit就是1.
引入新水果时可以直接用与或运算得知是否相克,并且快速得到不安全水果集合。

3.代码部分
#include
#include
#include
#include<stdio.h>
#include
#include
using namespace std;
int N, M, Com_F_num = 1;//Com_F_num是需要参加计算的水果数+1
int max_i[105];//【i,n】号节点能形成的最大团中节点数目
int Cur_Cost, Min_Cost = 0x7fffffff;//当前水果价值,最小水果价值
int Cur_Safe_N, Max_Safe_N = 0;//当前安全水果数量,最多安全水果数量
int Safe_Fruit_Price = 0;
int Fruit_Price[105];//按序记录水果价格
int num2id[105], id2num[1005];//输入顺序转ID,和ID转换输入顺序
long long bit_value[55];//基础数据,bit_value【i】表示第i位是1,其他为零
set in_fruit_list, all_safe_fruits;
vector<pair<int, int>> fruit_list;
vector Cur_Safe_Vector, Ans;
struct Fruit_Set
{
long long a, b;//位零表示安全,1表示不安全
};
Fruit_Set And(Fruit_Set &s1, Fruit_Set &s2)
{
Fruit_Set temp_s;
temp_s.a = s1.a | s2.a;
temp_s.b = s1.b | s2.b;
return temp_s;
}
Fruit_Set not_eat[105];//保存每种水果的相克水果
void Init()//对水果相克情况进行初始化
{//num是总的需要参加计算的水果数目
long long bit_move = 1;
for (int i = 1; i < 55; i++)
{
bit_value[i] = bit_move;
bit_move <<= 1;
}//按位赋值
for (auto it : fruit_list)//对相克水果初始化
{//it中保存的是id不是输入顺序
if (Fruit_Price[id2num[it.first]] > 0 && Fruit_Price[id2num[it.second]] > 0)//如果两个相克水果都在篮子里
{
if (id2num[it.first] > 50)
{
not_eat[id2num[it.second]].b |= bit_value[id2num[it.first] - 50];
}
else
{
not_eat[id2num[it.second]].a |= bit_value[id2num[it.first]];
}
if (id2num[it.second] > 50)
{
not_eat[id2num[it.first]].b |= bit_value[id2num[it.second] - 50];
}
else
{
not_eat[id2num[it.first]].a |= bit_value[id2num[it.second]];
}
}
}
return;
}
void my_io()//输入函数
{
cin >> N >> M;
for (int i = 0, a, b; i < N; i++)
{
cin >> a >> b;
fruit_list.push_back(pair<int, int>(a, b));
in_fruit_list.insert(a);
in_fruit_list.insert(b);
}
for (int i = 0, id, temp_price; i < M; i++)
{
cin >> id >> temp_price;
while (temp_price < 0);
if (in_fruit_list.find(id) == in_fruit_list.end())//如果不相克
{
all_safe_fruits.insert(id);//无须参加后续计算,直接加入安全水果篮子
Safe_Fruit_Price += temp_price;//记录安全水果价格
}
else
{
Fruit_Price[Com_F_num] = temp_price;
id2num[id] = Com_F_num;//id映射到输入顺序
num2id[Com_F_num++] = id;//输入顺序映射到ID
}
}
return;
}
void MaxT(int cur, Fruit_Set Cur_Safe)//求最大团,考察【cur, N】
{
if (Cur_Safe_N + max_i[cur + 1] + 1 < Max_Safe_N ||
((Cur_Safe_N + max_i[cur + 1] + 1 == Max_Safe_N) && (Cur_Cost + Fruit_Price[cur] > Min_Cost)))
return;
Cur_Safe_Vector.push_back(cur);//进入的一定安全
Cur_Safe_N++;
Cur_Cost += Fruit_Price[cur];
bool ret_flag = true;
for (int i = cur + 1; i < Com_F_num; i++)
{
if (i > 50 && ((Cur_Safe.b & bit_value[i - 50]) == 0))
{
MaxT(i, And(Cur_Safe, not_eat[i]));
ret_flag = false;
}
else if (i <= 50 && ((Cur_Safe.a & bit_value[i]) == 0))//安全
{
MaxT(i, And(Cur_Safe, not_eat[i]));
ret_flag = false;
}
}
if (ret_flag)
{
if (Cur_Safe_N > Max_Safe_N)
{
Ans = Cur_Safe_Vector;
Max_Safe_N = Cur_Safe_N;
Min_Cost = Cur_Cost;
}
else if (Cur_Safe_N == Max_Safe_N && (Cur_Cost < Min_Cost))
{
Ans = Cur_Safe_Vector;
Min_Cost = Cur_Cost;
}
}
Cur_Safe_Vector.pop_back();
Cur_Safe_N–;
Cur_Cost -= Fruit_Price[cur];
return;
}
int main()
{
my_io();
Init();
for (int i = Com_F_num - 1; i > 0; i–)
{
MaxT(i, not_eat[i]);
max_i[i] = Max_Safe_N;
}
for (auto it : Ans)
{
all_safe_fruits.insert(num2id[it]);
}
printf("%d\n", all_safe_fruits.size());
for (auto it = all_safe_fruits.begin();😉
{
printf("%03d", (*it));
it++;
if (it == all_safe_fruits.end())
break;
printf(" “);
}
if (Min_Cost == 0x7fffffff)
Min_Cost = 0;
printf(”\n%d", Min_Cost + Safe_Fruit_Price);
return 0;
}

4.结果和总结
在这里插入图片描述
这道题其实很早就有正确思路了,但是一个点就是过不了。检查了好几天才发现是一个数组减50是不该减的,这种低级错误要逐渐杜绝。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值