目录:
一:学习内容
贪心算法
外加补充stl内容:
1.queue
2.stack
二:用所学解决的问题
三:本周感想
学习内容:
queue:
头文件:#include< queue>
基本操作:
queue< int >q;
q.back()返回最后一个元素;
q.empty()如果队列为空则返回真;
q.front();返回第一个元素;
q.pop()删除第一个元素;
q.push()在末尾加入一个元素;
q.size()返回队列中元素的个数;
队列符合先进先出后进后出的规则;
内部默认数据存放容器为deque,若要用非默认容器初始化,必须要在模板中指定容器类型。
stack:
头文件:#include< stack >
基本用法:
stack< int>p;
p.empty()栈为空则返回真;
p.pop()移除栈顶元素;
p.push()在栈顶添加元素;
p.size()返回栈中元素数目;
p.top()返回栈顶元素;
stack的实现很简单,只需要将底层结构进行约束就可以了。
由于stack后进先出的规则,所以并无走访功能,且stack是没有迭代器的。
:贪心算法:
前面所整理的都是贪心算法所涉及到的所需要的具体工具;因为贪心算法就基本讲完了,这里就总结总结自己所消化的贪心算法。
个人感觉,贪心算法顾名思义,就是一种鼠目寸光只考虑当前最优,不考虑影响之后的算法,没什么固定的套路,就是要最优就完了。思维很重要,有的题你就是会考虑一天没啥感觉,一看题解恍然大悟,整的我很烦躁,当然也有基本的思路啦,比如把问题分解来看,寻找局部的最优解,然后结合成原来问题的最优解,但是据我搜索得来,贪心并不一定能得到某些问题的最优解,但也是最优解的近似。
(个人之见,思路有些乱,这大晚上的想到啥就写些啥呗)
在这写写做题心得叭:有些题一定要在纸上写写画画,刚才整理的一道贪心题就是画了半天才搞懂的(具体请看训练总结篇 第四周周结);再有就是,某个思路写的代码改了好几遍不通过就更该个思路,话不多说了整两个真题。
用所学解决的问题:
:最小新整数
描述
给定一个十进制正整数n(0 < n < 1000000000),每个数位上数字均不为0。n的位数为m。
现在从m位中删除k位(0<k < m),求生成的新整数最小为多少?
例如: n = 9128456, k = 2, 则生成的新整数最小为12456
输入
第一行t, 表示有t组数据;
接下来t行,每一行表示一组测试数据,每组测试数据包含两个数字n, k。
输出
t行,每行一个数字,表示从n中删除k位后得到的最小整数。
样例输入
2
9128456 2
1444 3
样例输出
12456
1
题目理解:就是很简单的删数问题,删掉原来k个数,在不改变顺序的情况下使得剩下的数最小。
一开始的思路就是从下标为0开始遍历,如果x[i]>x[i+1]就删掉第i个数知道删完k个为止,输出的时候再保证前面的0去掉就OK了感觉也蛮简单的,也就是以下这个代码
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
char m[100];
int t,k;
cin>>t;
while(t--)
{
cin>>m>>k;
int len=strlen(m);
for(int i=0; i<k; i++)
{
for(int j=0; j<len-1; j++)
{
if(m[j]>m[j+1])
{
for(int r=j; r<len-1; r++)
m[r]=m[r+1];
break;
}
}
len--;
}
int i=0;
while(m[i]!=0)
i++;
for(int j=i; j<len; j++)
cout<<m[j];
cout<<endl;
}
return 0;
}
但是没有通过,于是改了好久好久,感觉没啥错但就是不通过,个人认为应该就是多重for循环,复杂度高,所以后来写了while语句也就是下面的代码
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
int t, k, m;
char str[20];
cin >> t;
while(t--)
{
cin >> str >> k;
m = strlen(str);
while(k--)
{
for(int i = 0; i < m; i++)
if(str[i] > str[i+1])
{
for(int j = i; j<m; j++)
str[j]=str[j+1];
break;
}
m--;
}
cout<<str<<endl;
}
return 0;
}
虽然通过了但觉得不是很严谨,然后看了课件,尝试了另一种思路,就是从n个数中删掉k个换一种思维改成选择n-k个;因为要删掉k个数所以每次选择的数就是从第i个到k+1个数中选一个,下一次选择的数一定在这个数的后面所以下一次选就在本次x[i+1]到x[k+2]因为已经选过一个了所以还要再选n-k-1个所以就是到k+2之后依次类推,而且确实要除去前导0的,大概是测试数据的问题所以我那个题通过了。
Pearl Pairing
描述
At Bessie’s recent birthday party, she received N (2 <= N <= 100,000; N%2 == 0) pearls, each painted one of C different colors (1 <= C <= N).
Upon observing that the number of pearls N is always even, her creative juices flowed and she decided to pair the pearls so that each pair of pearls has two different colors.
Knowing that such a set of pairings is always possible for the supplied testcases, help Bessie perform such a pairing. If there are multiple ways of creating a pairing, any solution suffices.
输入
-
Line 1: Two space-separated integers: N and C
-
Lines 2…C + 1: Line i+1 tells the count of pearls with color i: C_i
输出 -
Lines 1…N/2: Line i contains two integers a_i and b_i indicating that Bessie can pair two pearls with respective colors a_i and b_i.
样例输入
8 3
2
2
4
样例输出
1 3
1 3
2 3
3 2
题目大意:有n个珍珠,每个珍珠涂上c钟不同颜色中的一种。
让你给珍珠配对让每对珍珠有两种不同的颜色;随意输出一种配对方式即可;
题目解析:
我想的就很简单就是记录每种颜色的珍珠有几个并记录他是第几种(也就是下标),然后从小到大按照颜色顺序大小排序(因为是随意的答案,一种感觉就觉得这样挺对,所以就这样写了),排序之后分为两组前n/2和后n/2。具体实现就是下面的代码了。但是是错误的。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
int n,c,a[100005],b[100005],l=1;
struct cc1
{
int c1;
int c2;
bool operator<(const cc1 b)
{
return c1>b.c1;
}
}cc[100005];
int main()
{
cin>>n>>c;
for(int i=1; i<=c; i++)
{cin>>cc[i].c1;
cc[i].c2=i;}
sort(cc+1,cc+c+1);
for(int i=1;i<=n/2;i++)
{
cc[l].c1--;
a[i]=cc[l].c2;
if(cc[i].c1==0)l++;
}
for(int i=1; i<=n/2; i++)
{ cc[l].c1--;
cout<<a[i]<<" "<<cc[l].c2<<endl;
if(cc[l].c1==0)l++;
}
}
因为这个代码错误试,改了很多次也不太对,所以大概是思路错了,所以又在纸上写了样例去尝试别的思路,然后发现因为数据保证存在答案,所以n一定会是偶数,并且每种颜色的珍珠也不可能超过n/2个,所以只需要枚举n/2个就可以了,其实也还是分为两组每次取两组的第i位,(有点我原来的思路优化一下的意思)至于要不要sort排序,好像这道题没必要因为存数组的时候就是按照从小到大存的。具体实现就是以下代码了。
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int n,c,s[100005],a,r;
int main()
{
cin>>n>>c;
for(int i=1;i<=c;i++)
{
cin>>a;
for(int j=1;j<=a;j++)
{
s[r]=i;
r++;
}
}
for(int i=0;i<n/2;i++)
cout<<s[i]<<" "<<s[i+n/2]<<endl;
return 0;
}
拼点游戏
描述
C和S两位同学一起玩拼点游戏。有一堆白色卡牌和一堆蓝色卡牌,每张卡牌上写了一个整数点数。C随机抽取n张白色卡牌,S随机抽取n张蓝色卡牌,他们进行n回合拼点,每次两人各出一张卡牌,点数大者获得三颗巧克力,小者获得一颗巧克力,如果点数相同,每人各得二颗巧克力,使用过的卡牌不得重复使用。已知C和S取到的卡牌点数,请编程计算S最多和最少能得到多少颗巧克力。
输入
输入包含多组测试数据。
每组测试数据的第一行是一个整数n(1<=n<=1000),接下来一行是n个整数,表示C抽到的白色卡牌的点数,下一行也是n个整数,表示S抽到的蓝色卡牌的点数。
输入的最后以一个0表示结束。
输出
对每组数据,输出一行,内容是两个整数用空格格开,分别表示S最多和最少可获得的巧克力数。
样例输入
3
92 83 71
95 87 74
2
20 20
20 20
2
20 19
22 18
0
样例输出
9 5
4 4
4 4
题目解析:
题意很简单,就是谁大加三分谁小加一分一样大都加两分,求分数的最大值和最小值;
我的思路呢最大值就是两个数组都sort从小到大排序,然后用s里面的数去依次和c的数去比,只要有比c得数大的就结果就加三如果没有就比是否有等于的就加二再就是小于了只能加一, 每次要有一个判断条件,看是否需要再比较等于或者小于。最小值呢就是两个数组反过来就好了。具体实现就是以下代码。但是是错误的。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main()
{
int n,i,c[1001],s[1001],cc[1001],ss[1001];
while(cin>>n)
{
memset(c,0,sizeof(c));
memset(cc,0,sizeof(cc));
memset(ss,0,sizeof(ss));
memset(s,0,sizeof(s));
if(n==0)
break;
for(i=0; i<n; i++)
{
cin>>c[i];
cc[i]=c[i];
}
for(i=0; i<n; i++)
{
cin>>s[i];
ss[i]=s[i];
}
sort(c,c+n);
sort(s,s+n);
sort(cc,cc+n);
sort(ss,ss+n);
int maxx=0,m=n;
for(int i=0; i<n; i++)
{
int pp=0;
for(int j=0; j<m; j++)
if(s[j]>c[i])
{
maxx+=3;
for(int k=j; k<m-1; k++)
s[k]=s[k+1];
m--;
pp=1;
break;
}
if(pp==1)
continue;
for(int j=0; j<m; j++)
if(s[j]==c[i])
{
maxx+=2;
for(int k=j; k<m-1; k++)
s[k]=s[k+1];
m--;
pp=1;
break;
}
if(pp==1)
continue;
for(int i=0; i<m-1; i++)
s[i]=s[i+1];
m--;
maxx+=1;
}
int minn=0,mm=n;
for(int i=0; i<n; i++)
{
int pp=0;
for(int j=0; j<mm; j++)
if(cc[j]>ss[i])
{
minn+=3;
for(int k=j; k<mm-1; k++)
cc[k]=cc[k+1];
mm--;
pp=1;
break;
}
if(pp==1)
continue;
for(int j=0; j<mm; j++)
if(cc[j]==ss[i])
{
minn+=2;
for(int k=j; k<mm-1; k++)
cc[k]=cc[k+1];
mm--;
pp=1;
break;
}
if(pp==1)
continue;
for(int i=0; i<mm-1; i++)
cc[i]=cc[i+1];
mm--;
minn+=1;
}
minn=4*n-minn;
cout<<maxx<<" "<<minn<<endl;
}
提交不对……哎,所以继续老办法找别的思路呗。
当我再读这个题的时候想到了高中课文田忌赛马的游戏,感觉就是同样的游戏啊。但是知道有啥用还是觉得我那个思路挺对的,但却是不行,不能固化思维,所以就在纸上验算样例,然后就发现原来方法比较麻烦不用盲目的挨个比较,只要s的最大的和c的最大的比 如果大就加3并移动下标到两个的次大的,否则就比较最小的,如果s大就加3并移动下标到次小,否则就比较s小和c大如果能相等加个2就加2不能就只能加一了,然后s小到次小c大到次大。也就是记录下标,让下标去移动。因为每次比较都一共有四块巧克力,所以s的最小就是4*n-c的最大,具体实现就是下面的代码。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int n;
int solve(int c[1005],int s[1005])
{
int ans=0;
int cl=1,cr=n,sl=1,sr=n;
while(sl<=sr)
{
if(s[sr]>c[cr])
{
--sr;
--cr;
ans+=3;
}
else if(s[sr]<c[cr])
{
++sl;
--cr;
ans+=1;
}
else
{
if(s[sl]>c[cl])
{
++sl;
++cl;
ans+=3;
}
else
{
if(s[sl]<c[cr])
{
++sl;
--cr;
ans+=1;
}
else
{
++sl;
--cr;
ans+=2;
}
}
}
}
return ans;
}
int main()
{
while(cin>>n,n)
{
int a[1005];
int b[1005];
for(int i=1; i<=n; ++i)
cin>>a[i];
for(int i=1; i<=n; ++i)
cin>>b[i];
sort(a+1,a+1+n);
sort(b+1,b+1+n);
cout<<solve(a,b)<<" "<<4*n-solve(b,a)<<endl;
}
}
本周感想:
贪心没啥固定套路所以思维显得尤为重要,但是题做多了那种思维也会一看题就会想出来,就算不对也就是优化优化就可以了,所以做题真的很重要。还有就是一定不能想到一种思路就局限在这种思路里面,提交不对那就跳出去,去重新读题换个角度去思考,可以尝试把问题转变成别的问法,然后自己去求解,蛮实用的。继续加油吧~~
We must accept finite disappointment, but we must never lose infinite hope.