题目A:选数问题
题面:
思路:此题的核心思想是枚举法。就是在n个数里找出所有的k个数的组合,然后判断k个数的和是否跟目标值相同.
最容易想到的想法是用k个循环找出所有的情况,但是显然这个做法是不行的,时间复杂度太高了。
那么用递归来解决就相对容易些。我们的目的是挑选k个数并且他们的和是S,那我们就对每个数进行一次判断,每个数我们都有选该数和不选该数地选项,当我们选择该数时,计数加一,在此基础上去判断下一个数(递归)选或者不选;当我们不选该数时,不计数,在此基础上去判断下一个数该不该选。那么接下来就是判断返回的条件,显而易见,当取得数等于k,和又恰好为S是符合条件的一种情况时,返回,不再从此基础上选择,进而后退一步做更多选择(此时固定前k-1步,变换第k步,依次递归);还有就是不符合要求的结果,还没选够k个,值就大于S啦,或者已经够K个了,值还没到S的啦,还有就是数字的索引超过数组个数,这些全部返回。
下面以样例为例画个示意图吧,每个数按照先不选后选的选择。
后面就以此类推吧,跟当初玩九连环的原理差不多,不停递归不停来回倒。
#include<iostream>
using namespace std;
int y;//合格搭配数量
int n,K,S;//定义数组个数 ,需要取的个数,需要得到的和
int *a=new int[10000];//存放输入的数组
void solve2(int i,int sum,int x)//i是当前数组索引 ,sum是未达目标 ,x是选中数字的个数
{//递归函数,用来计算有几种合格状态
if(x==K &&sum ==0)
{
y++;
return;
}
if(i>=n)
return;
if(x>K||sum<0) return;
solve2(i+1,sum,x);
x++;
solve2(i+1,sum-a[i],x);
x--;
}
int main()
{
int T;//输入组数
cin>>T;
for(int i=0;i<T;i++)//组循环
{
y=0;
cin>>n>>K>>S;
//cout<<n<<K<<S<<endl;
//cout<<1<<endl;
for(int j=0;j<n;j++)
{
// cout<<2<<endl;
cin>>a[j];
}
solve2(0,S,0);
cout<<y<<endl;
}
}
题目B 区间选点
题面:
思路:这个题目的关键是对区间排序,怎样排序能够保证后面的区间与前面的区间一旦不相交就一定得计一个新数。我一开始用的左边界排序,虽然可以通过简单的样例,但是通过画图来比较,他不能像右边界排序那样,保证从前往后遍历新出现的区间有不与前面区间重合的地方,假如我们把问题转化为以待比较区间的最右边界作为标记点,那么右边界排序可以保证每次新计数后被确定为待比较区间的右边界是新的标记点。
`
#include <iostream>
#include<algorithm>
using namespace std;
struct quyu
{
int x,y;
operator < (quyu a)
{
if(y!=a.y)
return y<a.y;
else
return x>a.x;
}
};
int main()
{
int n;
cin>>n;
quyu m[1000];
//int a,b;
for(int i=0;i<n;i++)
{
cin>>m[i].x>>m[i].y;
//cout<<m[i].x<<" "<<m[i].y<<endl;
}
sort(m,m+n);
/*for(int i=0;i<n;i++)
{
cout<<m[i].x<<" "<<m[i].y<<endl;
}*/
int ans=1;//记录点的个数 ,初始是在第一个区间里的一个点,当有与其不重叠的区间时,点数增加
int index=0;//记录当前被比较右界限区间的索引,初始是第一个区间
for(int i=1;i<n;i++)//需要将除第一个区间之外的其他区间全部比较
{
// cout<<1<<endl;
if(m[index].y<m[i].x)
{
index=i;
ans++;
}
/*else if(m[index].y==m[i].x)
{
if(i==n-2)
{
index=i;
ans++;
}
}*/
}
cout<<ans<<endl;
return 0;
}
题目C 区间覆盖
思路:这个题目我用的左边界排序,目的是将坐标起点作为待比较点,用区间的左边界与待比较点作对比,之后用每个区间的左边界与确定用来覆盖的最后一个区间的右边界+1进行比较,若左边界被包含,去看右边界,若右边界大于被作为预备覆盖区间的右边界,将此区间更新为新的预备覆盖区间(一句话概括就是找到与上一个区间相接的右边界最大的区间),所以每个区间有三次比较机会,最后一个覆盖区间一次(判断是否有可能成为预备役),预备役一次(判断是否是最好的预备役或者是否无法覆盖了,),最后一个覆盖区间一次,看看是否在覆盖区间更新之后可以成为新的预备役。
`
#include<iostream>
#include<algorithm>
using namespace std;
struct quyu{
int x,y;
bool operator <(quyu a)
{
if(x!=a.x)
return x<a.x;
else
return y<a.y;
}
};
int ans;//取的区间个数
int index;//待比较的区间界限
int n,t;//定义区域个数,区间终点
int in1;//index的预备役
quyu a[100000];//存放区域的数组
int main()
{
index=1;
ans=1;//初始化先取一个
cin>>n>>t;
for(int i=0;i<n;i++)
{
cin>>a[i].x>>a[i].y;
}
sort(a,a+n);
in1=0;
for(int i=0;i<n;i++)
{
if(a[i].x<=index&&a[i].y>=index)//前一个待比较区间的右边界在这个区间内
{
if(in1<a[i].y+1)//比较目前区间的最右边界跟前一个区间的最右边界相比是否更大,大的话更新
in1=a[i].y+1;//寻找在可以与前一个区间相连的y最大的区间
}
if(a[i].x>index)//当当前区间脱离最右边界时,将最右边界更新为index的预备役in1
{
index=in1;//现在再判断一下这个
if(a[i].x>index)
{
cout<<-1<<endl;
return 0;
}
ans++;
}
if(a[i].x<=index&&a[i].y>=index)//前一个待比较区间的右边界在这个区间内
{
if(in1<a[i].y+1)//比较目前区间的最右边界跟前一个区间的最右边界相比是否更大,大的话更新
in1=a[i].y+1;//寻找在可以与前一个区间相连的y最大的区间
}
/*else
{
cout<<-1<<endl;
return 0;
}*/
if(in1>t)
break;
}
if(in1<=t)//如果最后都没将t覆盖
{
cout<<-1<<endl;
return 0;
}
cout<<ans<<endl;
}
----------------------------我----是-----分-------割-------线---------------------------
如你所见,我是这个菜瓜的草稿箱,我出现的原因是这个憨瓜在第一篇博客里立了个flag。
于是现在来记录我的调试过程,先看那个大力出奇迹的题吧,这个简单点(划掉)。
我最开始的代码
#include<iostream>
#include<string>
#include<cstring>
#include<sstream>
#include<algorithm>
#include<iomanip>
using namespace std;
struct stu
{
string name;
// char score[8];
int num;//答出题数
int sum;//总耗时
bool operator <(const stu &a)const
{
if(num!=a.num)
return num>a.num;
if(sum!=a.sum)
return sum<a.sum;
int l1=name.length();
int l2=a.name.length();
for(int i=0;i<min(l1,l2);i++)
{
if(name[i]==a.name[i]) continue;
if(name[i]<a.name[i]) return true;
if(name[i]>a.name[i]) return false;
}
if(l1<l2) return true;
return false;
} //重载比较运算符,便于将学生排序输出
};//定义学生结构体
stu *kaosh=new stu;//定义学生结构体数组
int n,m;
int main()
{
cin>>n>>m;//题数和每次罚时
int i=0;//学生的索引
char s[1000]; //每个学生每道题的记录
while(cin>>s)//输入名字
{
kaosh[i].name=s;
kaosh[i].num=0;
kaosh[i].sum=0;
for(int j=0;j<n;j++)//输入记录
{
cin>>s;
int e=0;//该字符的索引
int l=strlen(s);
if(s[e]!='-'&&s[e]!='0')//当记录的是正数时
{
int um=0;
kaosh[i].num++;//答对题数+1
//e++;
//string ss;//存放算数的字符串
//int ee=0;//算数字符串的索引
while(e<l&&s[e]!='(')//遇到括号之前 (计算括号之前的分数)
{
//ss[ee]=s[e];
//ee++;
um=um*10+s[e]-'0';
e++;
}
//stringstream ssr;
//double a;
//ssr<<ss;
//ssr>>a;
kaosh[i].sum=kaosh[i].sum+um;
//ee=0;
um=0;
while(e<l&&s[e]!=')')//遇到括号之后(计算括号里的分数)/若没有括号后面为空,不进行循环
{
//ss[ee]=s[e];
//ee++;
um=um*10+s[e]-'0';
e++;
}
kaosh[i].sum=kaosh[i].sum+um*m;
}
else//当是负数或者为0时
{
if(s[e]=='-')
{
e++;
//string ss;//存放算数的字符串
//int ee=0;//算数字符串的索引
int um;
while(e<l)
{
//ss[ee]=s[e];
um=um*10+s[e]-'0';
}
// stringstream ssr;
// double a;
// ssr<<ss;
// ssr>>a;
kaosh[i].sum=kaosh[i].sum+um*m;
}
}
}
i++;
}
int z=i;//记录学生个数
sort(kaosh+1,kaosh+z);
for(int dd=0;dd<z;dd++)
{
cout<<left<<setw(10)<<kaosh[dd].name<<" ";
cout<<right<<setw(2)<<kaosh[dd].num<<" "<<setw(4)<<kaosh[dd].sum<<endl;
}
return 0;
}
嘶,没用的注释太多了,简化一下
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<iomanip>
using namespace std;
struct stu
{
string name;
int num;//答出题数
int sum;//总耗时
bool operator <(const stu &a)const
{
if(num!=a.num)
return num>a.num;
if(sum!=a.sum)
return sum<a.sum;
int l1=name.length();
int l2=a.name.length();
for(int i=0;i<min(l1,l2);i++)
{
if(name[i]==a.name[i]) continue;
if(name[i]<a.name[i]) return true;
if(name[i]>a.name[i]) return false;
}
if(l1<l2) return true;
return false;
} //重载比较运算符,便于将学生排序输出
};//定义学生结构体
stu *kaosh=new stu;//定义学生结构体数组
int n,m;
int main()
{
cin>>n>>m;//题数和每次罚时
int i=0;//学生的索引
char s[1000]; //每个学生每道题的记录
while(cin>>s)//输入名字
{
kaosh[i].name=s;
kaosh[i].num=0;
kaosh[i].sum=0;
for(int j=0;j<n;j++)//输入记录
{
cin>>s;
int e=0;//该字符的索引
int l=strlen(s);
if(s[e]!='-'&&s[e]!='0')//当记录的是正数时
{
int um=0;
kaosh[i].num++;//答对题数+1
while(e<l&&s[e]!='(')//遇到括号之前 (计算括号之前的分数)
{
//ss[ee]=s[e];
//ee++;
um=um*10+s[e]-'0';
e++;
}
kaosh[i].sum=kaosh[i].sum+um;
um=0;
while(e<l&&s[e]!=')')//遇到括号之后(计算括号里的分数)/若没有括号后面为空,不进行循环
{
um=um*10+s[e]-'0';
e++;
}
kaosh[i].sum=kaosh[i].sum+um*m;
}
else//当是负数或者为0时
{
if(s[e]=='-')
{
e++;
int um;
while(e<l)
{
um=um*10+s[e]-'0';
}
kaosh[i].sum=kaosh[i].sum+um*m;
}
}
}
i++;
}
int z=i;//记录学生个数
sort(kaosh+1,kaosh+z);
for(int dd=0;dd<z;dd++)
{
cout<<left<<setw(10)<<kaosh[dd].name<<" ";
cout<<right<<setw(2)<<kaosh[dd].num<<" "<<setw(4)<<kaosh[dd].sum<<endl;
}
return 0;
}
这样顺眼多了,然后我增加了几个测试点之后发现,分数计算模块出了几个问题:
1.e在判断完左括号之后没有加一,导致um在计算的时候将左括号误认为数字加入出现混乱
2.在计算括号里的内容时,e忘记++,导致循环无法终止
3.最最最严重的问题,一个在后面用测试数据跑才发现并且让我捶胸痛哭的就是,我把题意理解错了,此题未解的话不能算罚时,而我这个憨憨非但算了,还在一开始为看到测试数据里的输入-9999输出为0而感到无比的困惑感到费解。
4.数组开的太小,而且动态结构数组好像不合适,于是我一咬牙,给学生结构体还有s字符串各开了10000的空间,空间瞬间充实了
在处理完计算罚时板块之后,程序总算可以跑出结果了,于是在我不出所料的预测下,数据果然不对,在排除完上面那个提议理解错误之后,我将目光锁定了比较模块,在对重载函数进行了一系列辣手摧花的检验之后,洗清了他的嫌疑,那么排除所有的不可能,剩下的只有一个真相,那就是你——sort函数,最后通过控制变量法,sort以舍弃一个值为代价脱胎换骨成为了sort(kaosh,kaosh+z),顺便解释一下我的变量名。
天真的你以为到这里就结束了?在经历了CE添上了一个的头文件(之前被我调试过程中用其他函数时改成了string.h)之后,我盯着平台的WA陷入了深深的自我否定与自我怀疑。我打开我的编译器,看着我那伤痕累累满目疮痍的代码,不禁流下了悔恨的泪水,就在这模糊的泪光中,我仿佛看到一道光在我的屏幕上闪过,好像有一个声音在我耳畔响起:仅此一次,下不为例,再不要脸的把你的代码提交一遍,去吧!
我鬼使神差地照做了,于是
此时,我知道,是他,一位名为玄学的伟人曾经来过,在点化了迷茫的我之后又悄然离开,这是多么多么big的精神,big到数组爆裂。
下面再来回忆一下那位病友曾经的的憔悴模样,绑带缠身,十分令人心疼。
#include<iostream>
#include<string.h>
#include<string>
#include<cstring>
#include<algorithm>
#include<iomanip>
using namespace std;
struct stu
{
string name;
int num;//答出题数
int sum;//总耗时
bool operator <(const stu &a)const
{
if(num!=a.num)
{
return num>a.num;
}
if(sum!=a.sum)
{
return sum<a.sum;
}
/* int w;
w=strcmp(a.name,name);
if(w>0)
return true;
else
return false;*/
int l1=name.length();
int l2=a.name.length();
for(int i=0;i<min(l1,l2);i++)
{
// strcmp
if(name[i]==a.name[i]) continue;
if(name[i]<a.name[i]) return true;
if(name[i]>a.name[i]) return false;
}
if(l1<l2) return true;
return false;
} //重载比较运算符,便于将学生排序输出
}kaosh[10000];//定义学生结构体
//stu *kaosh=new stu;//定义学生结构体数组
int n,m;
int main()
{
cin>>n>>m;//题数和每次罚时
int i=0;//学生的索引
char s[100000]; //每个学生每道题的记录
while(cin>>s)//输入名字
{
kaosh[i].name=s;
// cout<<kaosh[i].name<<endl;
kaosh[i].num=0;
kaosh[i].sum=0;
for(int j=0;j<n;j++)//输入记录
{
// cout<<"**********"<<endl;
cin>>s;
int e=0;//该字符的索引
int l=strlen(s);
if(s[e]!='-'&&s[e]!='0')//当记录的是正数时
{
// cout<<"&&&&&&&&"<<endl;
int um=0;
kaosh[i].num++;//答对题数+1
while(e<l&&s[e]!='(')//遇到括号之前 (计算括号之前的分数)
{
um=um*10+s[e]-'0';
e++;
}
// cout<<um<<endl;
kaosh[i].sum=kaosh[i].sum+um;
// cout<<kaosh[i].sum<<endl;
e++;
um=0;
while(e<l&&s[e]!=')')//遇到括号之后(计算括号里的分数)/若没有括号后面为空,不进行循环
{
um=um*10+s[e]-'0';
e++;
}
// cout<<um<<endl;
kaosh[i].sum=kaosh[i].sum+um*m;
// cout<<kaosh[i].sum<<" "<<kaosh[i].num<<endl;
}
/* else//当是负数或者为0时
{
if(s[e]=='-')
{
// cout<<"^^^^^^^^"<<endl;
e++;
int um=0;
while(e<l)
{
um=um*10+s[e]-'0';
e++;
}
// cout<<um<<endl;
kaosh[i].sum=kaosh[i].sum+um*m;
// cout<<kaosh[i].sum<<endl;
// cout<<kaosh[i].num<<endl;
}
}*/
}
i++;
}
int z=i;//记录学生个数
// cout<<z<<endl;
sort(kaosh,kaosh+z);
for(int i=0;i<z;i++)
{
cout<<std::left<<setw(10)<<kaosh[i].name<<" ";
cout<<std::right<<setw(2)<<kaosh[i].num<<" "<<setw(4)<<kaosh[i].sum<<endl;
}
return 0;
}
不过现在他已恢复健康:
#include<iostream>
#include<string.h>
#include<string>
#include<cstring>
#include<algorithm>
#include<iomanip>
using namespace std;
struct stu
{
string name;
int num;//答出题数
int sum;//总耗时
bool operator <(const stu &a)const
{
if(num!=a.num)
{
return num>a.num;
}
if(sum!=a.sum)
{
return sum<a.sum;
}
int l1=name.length();
int l2=a.name.length();
for(int i=0;i<min(l1,l2);i++)
{
if(name[i]==a.name[i]) continue;
if(name[i]<a.name[i]) return true;
if(name[i]>a.name[i]) return false;
}
if(l1<l2) return true;
return false;
} //重载比较运算符,便于将学生排序输出
}kaosh[10000];//定义学生结构体
int n,m;
int main()
{
cin>>n>>m;//题数和每次罚时
int i=0;//学生的索引
char s[100000]; //每个学生每道题的记录
while(cin>>s)//输入名字
{
kaosh[i].name=s;
kaosh[i].num=0;
kaosh[i].sum=0;
for(int j=0;j<n;j++)//输入记录
{
cin>>s;
int e=0;//该字符的索引
int l=strlen(s);
if(s[e]!='-'&&s[e]!='0')//当记录的是正数时
{
int um=0;
kaosh[i].num++;//答对题数+1
while(e<l&&s[e]!='(')//遇到括号之前 (计算括号之前的分数)
{
um=um*10+s[e]-'0';
e++;
}
kaosh[i].sum=kaosh[i].sum+um;
e++;
um=0;
while(e<l&&s[e]!=')')//遇到括号之后(计算括号里的分数)/若没有括号后面为空,不进行循环
{
um=um*10+s[e]-'0';
e++;
}
kaosh[i].sum=kaosh[i].sum+um*m;
}
}
i++;
}
int z=i;//记录学生个数
sort(kaosh,kaosh+z);
for(int i=0;i<z;i++)
{
cout<<std::left<<setw(10)<<kaosh[i].name<<" ";
cout<<std::right<<setw(2)<<kaosh[i].num<<" "<<setw(4)<<kaosh[i].sum<<endl;
}
return 0;
}
害,然后是那个瑞神打牌的。
我一开始尝试用链表,源代码我就不放了,但是调试半天我没调出来。于是愤而换策略,数组简直太香了。
依旧是建立扑克牌结构体,建立花色和数字转换函数,但是在我按照原本的思路敲完代码之后我发现了我的第一个致命错误:发牌的题意理解错了,我天真的以为发排顺序是从一个人开始,一人拿13张,于是我开始的发牌循环就是4×13,结果能对才有鬼嘞,应该是发13个循环,四个人每次拿一张,13×4。
然后就是我在输入数组的时候,最开始采用设a,b两个数组分别存储两行数据,然后再用一个c把a和b连接起来,结果验证发现这个方法极其不稳定,先是莫名其妙多了一个“H”,后来就是运行出问题,而且在不影响存储的前提下改变a、b、c数组的大小竟然直接导致输出不稳定,于是果断放弃,选择把c变成一个中转变量,随着扑克牌的发放分别指代不同的数组a、b。(后来当我知道我的室友大多采用边输边存的策略时,我有一丝惆怅)
之后我又遇到了新的挑战,一开始我把花色和数字的转换调用放在了结构体的重载函数里,这意味着我比较的时候要进行n^2次转换,于是我把它们放在了输入模块,这样转换减少到n次。
之后我遇到了我的最后一个致命错误,这一次还是跟题意理解有关。关于输出模式,题目说每一组数最后多一空行,于是我这个憨憨就理所当然地理解为了输出的每一行数据后面多一空行(我竟然以为一行数据是一组!!!)
下面是我最终通过的版本。
#include<iostream>
#include<string.h>
#include<cstring>
#include<algorithm>
#include<iomanip>
using namespace std;
//char * str_bin(char* a, char* b);
int suitturn(char suit)
{
if(suit=='C') return 15;
if(suit=='D') return 16;
if(suit=='S') return 17;
if(suit=='H') return 18;
}
int numturn(char num)
{
if(num=='T') return 10;
if(num=='J') return 11;
if(num=='Q') return 12;
if(num=='K') return 13;
if(num=='A') return 14;
if(num>'1'||num<='9') return num-'0';
}
struct node{
char suit;//花色,C(梅花)D(方片)S(黑桃)H(红桃)
char num;//扑克牌数值
int x,y;
// void turn ()
//{
// x=suitturn(suit);
// y=numturn(num);
//}
bool operator < ( node &s)
{
//turn();
//s.turn();
if(x!=s.x)
return x<s.x;
if(y!=s.y)
return y<s.y;
}
};//单张扑克牌
struct men{
node poker[100];//扑克牌数组
int former;//存储下一个人的索引
}man[4];
int main()
{
char n;//发牌人
cin>>n;
man[0].former=1;
man[1].former=2;
man[2].former=3;
man[3].former=0;//标好索引
// cout<<n<<endl;;
char a[1000];
char b[1000];//分别存储两行牌
//cout<<b<<endl;
int xu;//作为 man数组的索引
while(n!='#')
{
cin>>a>>b;//输入两行牌
// cout<<"*************整体循环"<<endl;
if(n=='S')
xu=1;
else if(n=='W')
xu=2;
else if(n=='N')
xu=3;
else
xu=0;
int s1=0;//字符串的索引
//char c[10000];
//strcat(c,a);
//strcat(c,b);
// cout<<c<<endl;
char c[100];
strcpy(c,a);
// cout<<c<<endl;
for(int i=0;i<13;i++)
{
//cout<<"总轮循环****************"<<endl;
// cout<<"输出当前数组"<<c<<endl;
for(int j=0;j<4;j++)
{
// cout<<"单轮循环"<<endl;
man[xu].poker[i].suit=c[s1];
man[xu].poker[i].x=suitturn(c[s1]);
// cout<<c[s1]<<endl;
// cout<<man[xu].poker[i].suit<<endl;
s1++;
man[xu].poker[i].num=c[s1];
man[xu].poker[i].y=numturn(c[s1]);
// cout<<c[s1]<<endl;
s1++;
if(s1==52)
{
s1=0;
strcpy(c,b);
}
xu=man[xu].former;
// cout<<"单轮循环结束"<<endl;
}
//sort(man[xu].poker,man[xu].poker+13);
}
// cout<<"扑克牌循环结束***************"<<endl;
for(int i=0;i<4;i++)
{
sort(man[i].poker,man[i].poker+13);
// cout<<"输出循环"<<endl;
if(i==0)
cout<<"South player:"<<endl;
else if(i==1)
cout<<"West player:"<<endl;
else if(i==2)
cout<<"North player:"<<endl;
else
cout<<"East player:"<<endl;
//cout<<endl;
cout<<"+---+---+---+---+---+---+---+---+---+---+---+---+---+"<<endl;
//cout<<endl;
for(int j=0;j<13;j++)
{
cout<<"|"<<man[i].poker[j].num<<" "<<man[i].poker[j].num;
}
cout<<"|"<<endl;
//cout<<endl;
for(int j=0;j<13;j++)
{
cout<<"|"<<" "<<man[i].poker[j].suit<<" ";
}
cout<<"|"<<endl;
//cout<<endl;
for(int j=0;j<13;j++)
{
cout<<"|"<<man[i].poker[j].num<<" "<<man[i].poker[j].num;
}
cout<<"|"<<endl;
//cout<<endl;
cout<<"+---+---+---+---+---+---+---+---+---+---+---+---+---+"<<endl;
//cout<<endl;
}
cout<<endl;
cin>>n;
}
return 0;
}
最后我想说,基础不牢,地动山摇,粗心一时爽,debug火葬场。