题目:
在地下室里放着n种颜色的手套,手套分左右手,但是每种颜色的左右手手套个数不一定相同。A先生现在要出门,所以他要去地下室选手套。但是昏暗的灯光让他无法分辨手套的颜色,只能分辨出左右手。所以他会多拿一些手套,然后选出一双颜色相同的左右手手套。现在的问题是,他至少要拿多少只手套(左手加右手),才能保证一定能选出一双颜色相同的手套。
给定颜色种数n(1≤n≤13),同时给定两个长度为n的数组left,right,分别代表每种颜色左右手手套的数量。数据保证左右的手套总数均不超过26,且一定存在至少一种合法方案。
思路:
此题用到了贪心算法,对我而言可能是一个比较新的算法。之前可能接触到过,但是印象不太深。
我们来看这道题的解法:
有n种颜色,并且手套分左右手,我们最后要能得到一双完整的手套(左手+右手,并且颜色相同)。
保证成对存在的手套且每个颜色都覆盖的话,
即
对于非0(递增)序列a1,a2...an,要想最终取值覆盖每一个种类 n =sum(a1...an) -a1 +1(也就是总数减去最小值之后加一)
为什么会是这样呢?(只能通过举例子了)
倘若我们有两样水果放在一个黑箱里,苹果4个,橙子7个。我们至少要拿几个,才能保证两样水果个有一个呢?
运气好的话,我们那两个,苹果橙子各一个。要是运气不好呢?拿4个吗?万一全是橙子呢?那怎么办呢?我们先拿7个,这可能有好几种情况,但是最糟糕的情况就是我们全拿的是橙子,因此我们在拿一个肯定是苹果。回到我们这个题目,可以理解一下得到所有颜色需要那多少个手套。
但是注意,题目中说明,某种颜色的手套可能只有左手或者只有右手。,所以呢?为了避免不匹配,我们要假设全这些手套,全部拿走。到这里我们的思路就成型了
总结:
//求最少的的手套数量,并且保证至少有一个是成对的。
//假想理想的情况
//把左手没有或者右手没有的手套全部拿走,剩下的手套必定能成对。
//对于成对的手套要想最终取值覆盖每一个种类
//求出了左右手能覆盖必定成对手套的所有颜色的数量,选取最小的那个
//我们求出的方案就是 全部不能成套的手套+左右手必定能成对的数量且覆盖所有颜色的最小的那个+另外一边的随便一只手套
代码实现 :
int findMinimum(int n, vector<int> left, vector<int> right) {
int sum=0;
int left_sum=0;
int right_sum=0;
int left_min=INT_MAX;
int right_min=INT_MAX;
for(int i=0;i<n;++i)
{
if(left[i]==0||right[i]==0)
{
sum+=(left[i]+right[i]);
}
else
{
left_sum+=left[i];
right_sum+=right[i];
left_min=left_min>left[i]?left[i]:left_min;
right_min=right_min>right[i]?right[i]:right_min;
}
}
return sum+min((left_sum-left_min+1),(right_sum-right_min+1))+1;
}
题目:两地调度
公司计划面试 2N 人。第 i 人飞往 A 市的费用为 costs[i][0],飞往 B 市的费用为 costs[i][1]。
返回将每个人都飞到某座城市的最低费用,要求每个城市都有 N 人抵达。
输入输出:
输入:[[10,20],[30,200],[400,50],[30,20]]
输出:110
解释:
第一个人去 A 市,费用为 10。
第二个人去 A 市,费用为 30。
第三个人去 B 市,费用为 50。
第四个人去 B 市,费用为 20。最低总费用为 10 + 30 + 50 + 20 = 110,每个城市都有一半的人在面试。
思路:
我们不能按照每个城市每个人的排序进行选择,因为可能会有一些重叠。我们可以用数组组织每个人去A市的费用减去去B市的费用。两者相差越大,其结果越小。我们对此进行排序。前n个去A市,后面的去B市。将此思路进行扩展,写出代码。
代码:
int twoCitySchedCost(vector<vector<int>>& costs) {
if(costs.empty())
return 0;
int ret=0;
vector<int> arr;
for(int i=0;i<costs.size();++i)
{
ret+=costs[i][0]+costs[i][1];
arr.push_back(costs[i][0]-costs[i][1]);
}
sort(arr.begin(),arr.end());
for(int i=costs.size()-1;i>=0;--i)
{
if(i>=costs.size()/2)
ret-=arr[i];
else
ret+=arr[i];
}
return ret/2;
}
int twoCitySchedCost(vector<vector<int>>& costs) {
if(costs.empty())
return 0;
//假设所有人全去A市
//每个人去A 市的费用减去去B是的费用,排列好顺序(从小到大)
//该值越大说明那个人去A市花费越贵,而去B市比较便宜
int ret=0;
vector<int> arr;
for(int i=0;i<costs.size();++i)
{
ret+=costs[i][0];
arr.push_back(costs[i][0]-costs[i][1]);
}
sort(arr.begin(),arr.end());
for(int i=costs.size()-1;i>=costs.size()/2;--i)
{
ret-=arr[i];
}
return ret;
}
题目:玩筹码
数轴上放置了一些筹码,每个筹码的位置存在数组 chips 当中。
你可以对 任何筹码 执行下面两种操作之一(不限操作次数,0 次也可以):
将第 i 个筹码向左或者右移动 2 个单位,代价为 0。
将第 i 个筹码向左或者右移动 1 个单位,代价为 1。
最开始的时候,同一位置上也可能放着两个或者更多的筹码。返回将所有筹码移动到同一位置(任意位置)上所需要的最小代价。
输入输出:
输入:chips = [2,2,2,3,3]
解释:第1、2、3个筹码放在二号位置,第4、5个筹码放在三号位置
输出:2 解释:第四和第五个筹码移动到位置二的代价都是 1,所以最小总代价为 2。
思路:
将第 i 个筹码向左或者右移动 2 个单位,代价为 0。我们可以用这条规则,将奇数位置的筹码移到1号位置,代价为0;将偶数位置的筹码移到2号位置,代价为0;
将第 i 个筹码向左或者右移动 1 个单位,代价为 1。我们可以将一号位置的筹码移到二号位置,代价为一号筹码个数。我们可以将二号位置的筹码移到一号位置,代码为二号筹码个数。最小代价,就是选出最小的那个数。
代码:
int minCostToMoveChips(vector<int>& chips) {
if(chips.empty())
return 0;
int evenCount=0;
int oddCount=0;
for(int i=0;i<chips.size();++i)
{
if(chips[i]%2==0)
{
evenCount++;
}
else
{
oddCount++;
}
}
return min(evenCount,oddCount);
}
手套问题:
链接:https://www.nowcoder.com/questionTerminal/365d5722fff640a0b6684391153e58d8
两地调度问题:
链接:https://leetcode-cn.com/problems/two-city-scheduling
玩筹码问题:
链接:https://leetcode-cn.com/problems/play-with-chips