N只小白鼠(1 <= N <= 100),每只鼠头上戴着一顶有颜色的帽子。
现在称出每只白鼠的重量,要求按照白鼠重量从大到小的顺序输出它们头上帽子的颜色。
帽子的颜色用“red”,“blue”等字符串来表示。
不同的小白鼠可以戴相同颜色的帽子。白鼠的重量用整数表示。
多案例输入,每个案例的输入第一行为一个整数N,表示小白鼠的数目。
下面有N行,每行是一只白鼠的信息。第一个为不大于100的正整数,表示白鼠的重量;
第二个为字符串,表示白鼠的帽子颜色,字符串长度不超过10个字符。
注意:白鼠的重量各不相同。
每个案例按照白鼠的重量从大到小的顺序输出白鼠的帽子颜色。
3
30 red
50 blue
40 green
blue
green
red
用结构体储存每一个白鼠的重量和帽子颜色,sort排序
#include
using namespace std;
struct node{
int weight;
char color[15];
bool operator<(const node x){
return weight>x.weight;
}
}w[1005];
int main(){
int n;
while(~scanf("%d",&n)){
for(int i=1;i<=n;++i){
scanf("%d %s",&w[i].weight,w[i].color);
}
sort(w+1,w+n+1);
for(int i=1;i<=n;++i){
printf("%s\n",w[i].color);
}
}
return 0;
}
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?
(用K表示)5,1,1和1,5,1 是同一种分法。
每行均包含二个整数M和N,以空格分开。1<=M,N<=10。
对输入的每组数据M和N,用一行输出相应的K。
7 3
8
比较容易想到的
递归每次放置的苹果数升序或者降序,便可以去除重复的分法
如每次递归传三个参数now(当前剩下的苹果数),n(当前剩下的盘中数),x(当前盘中最少放置的苹果数)
当now=0时返回1,也就是求出了一种分法
如果now!=0但是n=0说明此分法无意义,返回0
#include
using namespace std;
int dfs(int now,int n,int x){
if(now==0)
return 1;
if(n==0)
return 0;
int ans=0;
for(int i=x;i<=now;++i){
ans+=dfs(now-i,n-1,i);
}
return ans;
}
int main(){
int n,m;
while(~scanf("%d %d",&m,&n)){
printf("%d\n",dfs(m,n,1));
}
return 0;
}
另一种思路是考虑苹果和盘子的关系
递归函数f(m,n)表示m个苹果放n个盘中里
当m
即f(m,n)=f(m,m);
当m>=n
苹果充足,可以分两种情况考虑
即f(m,n)=f(m-n,n)+f(m,n-1);
#include
using namespace std;
int dfs(int m,int n){
if(m==0||n==1)
return 1;
if(m<n)
return dfs(m,m);
else
return dfs(m,n-1)+dfs(m-n,n);
}
int main(){
int n,m,t;
scanf("%d",&t);
while(t--){
scanf("%d %d",&m,&n);
printf("%d\n",dfs(m,n));
}
return 0;
}
现有公园游船租赁处请你编写一个租船管理系统。
当游客租船时,管理员输入船号并按下S键,系统开始计时;
当游客还船时,管理员输入船号并按下E键,系统结束计时。
船号为不超过100的正整数。当管理员将0作为船号输入时,
表示一天租船工作结束,系统应输出当天的游客租船次数和平均租船时间。
注意:由于线路偶尔会有故障,可能出现不完整的纪录,即只有租船没有还船,
或者只有还船没有租船的纪录,系统应能自动忽略这种无效纪录。
测试输入包含若干测试用例,每个测试用例为一整天的租船纪录,格式为:
船号(1~100) 键值(S或E) 发生时间(小时:分钟)
每一天的记录保证按时间递增的顺序给出。当读到船号为-1时,全部输入结束,相应的结果不要输出。
对每个测试用例输出1行,即当天的游客租船次数和平均租船时间(以分钟为单位的精确到个位的整数时间)。
1 S 08:10
2 S 08:35
1 E 10:00
2 E 13:16
0 S 17:00
0 S 17:00
3 E 08:10
1 S 08:20
2 S 09:00
1 E 09:20
0 E 17:00
-1
2 196
0 0
1 60
用一个数组存储船的被租时间(如vis[i]表示编号为i的船的被租时间)
当输入的信息是租船信息
当输入的信息是还船信息
//每一天的租还情况是独立的,所以每次输出后初始化
…
#include
using namespace std;
int vis[105];//船被借出的时间
int main()
{
int a,b,c,num=0,cnt=0;
char x;
while(~scanf("%d",&a)){
if(a==-1)
break;
getchar();//获取空格
scanf("%c %d:%d",&x,&b,&c);
if(a==0){
if(num==0){
printf("0 0\n");
}else{
printf("%d %d\n",num,(cnt+num-1)/num);//向上取整
}
num=0,cnt=0;//清空数据
memset(vis,0,sizeof(vis));
continue;
}
if(x=='S'){//租船
if(vis[a]==0){
vis[a]=b*60+c;
}
}else{//还船
if(vis[a]){
++num;
cnt+=b*60+c-vis[a];
vis[a]=0;
}
}
}
return 0;
}
输入一个数n,然后输入n个数值各不相同,调换数组中最大和最小的两个数,然后输出。
测试数据有多组,输入n(1<=n<=20),接着输入n个数。
对于每组输入,输出交换后的结果。
2
1 3
3 1
循环找到最小值和最大值的下标…交换…输出
#include
using namespace std;
int s[105];
int main()
{
int n,a,aa,b,bb;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;++i){
scanf("%d",&s[i]);
}
a=b=s[1],aa=bb=1;
for(int i=2;i<=n;++i){
if(s[i]>a){
a=s[i];
aa=i;
}else if(s[i]<b){
b=s[i];
bb=i;
}
}
swap(s[aa],s[bb]);
for(int i=1;i<n;++i){
printf("%d ",s[i]);
}
printf("%d\n",s[n]);
}
return 0;
}
在一个果园里,小明已经将所有的水果打了下来,并按水果的不同种类分成了若干堆,小明决定把所有的水果合成一堆。
每一次合并,小明可以把两堆水果合并到一起,消耗的体力等于两堆水果的重量之和。
当然经过 n‐1 次合并之后,就变成一堆了。小明在合并水果时总共消耗的体力等于每次合并所耗体力之和。
假定每个水果重量都为 1,并且已知水果的种类数和每种水果的数目。
你的任务是设计出合并的次序方案,使小明耗费的体力最少,并输出这个最小的体力耗费值。
例如有 3 种水果,数目依次为 1,2,9。可以先将 1,2 堆合并,新堆数目为3,耗费体力为 3。
然后将新堆与原先的第三堆合并得到新的堆,耗费体力为 12。
所以小明总共耗费体力=3+12=15,可以证明 15 为最小的体力耗费值。
每组数据输入包括两行,第一行是一个整数 n(1<=n<=10000),表示水果的种类数。
第二行包含 n 个整数,用空格分隔,第 i 个整数(1<=ai<=1000)是第 i 种水果的数目。
对于每组输入,输出一个整数并换行,这个值也就是最小的体力耗费值。
输入数据保证这个值小于 2^31。
3
9 1 2
0
15
贪心思想,每次将最小的两堆果子合并,直到最后只剩一堆果子
正确性证明
果子的合并可以看成树节点的合并如图
最后会得到一棵二叉树,而耗费的体力值就等于每一个叶子节点到根节点的距离×该节点果子重量(这是显然的)
要最小化体力值也就是让更重的果子离根节点越近(权重思想)(哈夫曼树思想)
所以贪心策略可行
如果每一次合并都排序一次肯定会超时
所以用优先队列处理
优先队列详解
#include
using namespace std;
int main()
{
int n,x;
while(~scanf("%d",&n)&&n){
priority_queue<int,vector<int>,greater<int> >pq;
for(int i=1;i<=n;++i){
scanf("%d",&x);
pq.push(x);
}
int ans=0;
while(pq.size()>1){
int a=pq.top();
pq.pop();
int b=pq.top();
pq.pop();;
ans+=(a+b);
pq.push(a+b);
}
printf("%d\n",ans);
}
return 0;
}
其实还有更优的解法…时间复杂度为O(n)╰(●’◡’●)╮
不过这个题目数据不是很大,用优先队列就行
牌只有1到9,手里拿着已经排好序的牌a,对方出牌b,用程序判断手中牌是否能够压过对方出牌。
规则:出牌牌型有5种。
[1] 一张 如4 则5…9可压过
[2] 两张 如44 则55,66,77,…,99可压过
[3] 三张 如444 规则如[2]
[4] 四张 如4444 规则如[2]
[5] 五张 牌型只有12345 23456 34567 45678 56789五个,后面的比前面的均大。
输入有多组数据。
每组输入两个字符串(字符串大小不超过100)a,b。
a字符串代表手中牌,b字符串代表出的牌。
压过输出YES 否则NO。
12233445566677
33
YES
模拟题
用数组保存字符串a中每一张牌出现的次数
按字符串b的长度分情况讨论
#include
using namespace std;
char a[105],b[105];
int aa[15];
int main()
{
while(~scanf("%s %s",a+1,b+1)){
memset(aa,0,sizeof(aa));
int la=strlen(a+1),lb=strlen(b+1);
for(int i=1;i<=la;++i)
++aa[a[i]-'0'];
int ju=0;
if(lb>=1&&lb<=4){
for(int i=b[1]-'0'+1;i<=9;++i){
if(aa[i]>=lb){
ju=1;
break;
}
}
}else{
for(int i=b[1]-'0'+1;i+4<=9;++i){
if(aa[i]&&aa[i+1]&&aa[i+2]&&aa[i+3]&&aa[i+4]){
ju=1;
break;
}
}
}
if(ju)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
把3个相同的小三角形按如下方式连接起来就形成了一个一级三角阵。
我们把位于顶端的小三角形标记为T,位于左端的小三角形标记为L,位于右端的小三角形标记为R。 把3个一级三角阵按同样的方式连接起来就形成了一个二级三角阵。
我们为顶端的三角阵的标记添加前缀T,为左端的三角阵的标记添加前缀L,为右端的三角阵的标记添加前缀R。 把3个二级三角阵按同样的方式连接起来就形成了一个三级三角阵。
同样地为顶端的三角阵的标记添加前缀T,为左端的三角阵的标记添加前缀L,为右端的三角阵的标记添加前缀R。 依次类推,可以构建一个N级三角阵。 如果两个小三角形有公共点,则认为这两个小三角形相邻,可以一步到达。 你的任务是求从一个小三角形走到另一个小三角形至少需要多少步。
第一行是三角阵的等级N(N≤30)。
第二行和第三行都是一个长度为N的字符串,由大写字母“L”、“R”、“T”组成,表示两个小三角形的标号。
输出 1 个数,表示从一个小三角形走到另一个小三角形所需的最少步数。
3
TRL
RLR
5
从“TRL”出发,途经“TRR”、“RTT”、“RTL”、“RLT”,最后到达“RLR”,一共需要5步。
先找到起点和终点所在三角形不同的那一层,然后可以发现最短路径有两条
最短路径就是1+2和3+4+5中最短的那一个
重要的就是路径1,2,3,5的求法(但是不知道如何解释这个求法…只能略过了…π__π)
#include
#define ll long long
using namespace std;
int main()
{
int n,st=0;
char a[35],b[35];
scanf("%d%s%s",&n,a+1,b+1);
for(int i=1;i<=n;++i){
if(a[i]!=b[i]){
st=i;
break;
}
}
if(st==0){
printf("0\n");
return 0;
}
ll num=pow(2,n-st-1),ans1=1,ans2;
for(int i=st+1;i<=n;++i){
if(a[i]!=b[st])
ans1+=num;
if(b[i]!=a[st])
ans1+=num;
num>>=1;
}
num=pow(2,n-st-1);
ans2=pow(2,n-st)+1;
for(int i=st+1;i<=n;++i){
if(a[i]==a[st]||a[i]==b[st])
ans2+=num;
if(b[i]==b[st]||b[i]==a[st])
ans2+=num;
num>>=1;
}
printf("%lld\n",min(ans1,ans2));
return 0;
}
在寂寞的夜里,星象仪是非常浪漫的东西。但是,你作为一个精神稍微有点不太正常的Geek,把原本正常的星象仪改造得像电报发送器一样。当然,你这个的构造还要更加奇葩一点。具体来说,你的星象仪是一棵满二叉树,二叉树的节点都是有两个输入端和一个输出端的AND 门或者OR 门。它们输入和输出的信号都是只是0 或者1。它们会接受子节点的输出信号,然后将这两个信号进行AND 运算或者OR 运算作为自己的输出。然后,根节点的输出信号就是整个星象仪的输出信号。叶节点的输入信号是由你来调整的,如果二叉树有K 层,那么你显然有2K个输入信号可以调整。调整一次当然只能改变一个输入信号。根据你的设定,在一开始所有的输入端的输入信号都是0。现在你希望用星象仪得到一串信号,为此,你需要不停地调整输入。
假定你想要用上图中的星象仪得到输出信号000111,一种可行的方案是0001→0011→1100→1111→1010→0101,但是这样你要调整14 次输入信号。更加方便的方式是0000→0000→0000→0101→0101→0101,这样你总计只需要调整2次输入信号。 由于调整输入信号是一件非常麻烦的事情,现在你希望知道对于一台给定的星象仪,如果想要得到一串给定的信号,至少需要调整多少次输入。
输入文件包含多组测试数据。第一行有一个整数T,表示测试数据的组数。
每组测试数据的第一行是一个正整数 N,表示输入信号的数目。保证N 是2 的整数次幂。
第二行含有一个由 0 和1 组成的字符串S,表示你想要得到的信号。
第三行包含 N – 1 个整数,按照层次遍历顺序给出满二叉树的每个节点。整数只会是0或者1。0 表示二叉树的这个位置是一个OR 门,1 表示是一个AND 门。(T≤100,N≤8192,S的长度在10000 之内)
对于每组测试数据,在单独的一行内输出结果。
2
4
010101
0 0 0
4
111111
1 1 1
5
4
注:二叉树的层序遍历保存在一个数组中,节点i的左右子树为i×2 和 i×2+1
当信号仪的信号第一次改变后,后续的变化只需要调整一个输入就可以
所以只需要求出信号第一次从0变成1所需的输入次数再加上后续信号改变次数就是答案
对于信号从初始状态变成1所需的输入次数用递归实现
与门从0变1需要改变两个子节点的输入,或门从0变1需要改变一个子节点的输入…层层递归下去
#include
using namespace std;
const int maxn=1e5+5;
int tr[maxn],n;
char s[maxn];
int dfs(int now,int v){
if(v==0)
return 0;
if(now*2>=n){
if(tr[now])
return 2;
return 1;
}
if(tr[now])//与门需两边置1
return dfs(now<<1,v)+dfs(now<<1|1,v);//左移运算符
else//或门选最小值
return min(dfs(now<<1,v),dfs(now<<1|1,v));
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
scanf("%s",s+1);
int ls=strlen(s+1),num=0;
s[0]='0';
for(int i=1;i<n;++i){
scanf("%d",&tr[i]);
}
for(int i=1;i<=ls;++i)
if(s[i]!=s[i-1])
++num;
if(num==0){
printf("0\n");
}else{
printf("%d\n",dfs(1,1)+num-1);
}
}
return 0;
}
在人气动漫《某科学的超电磁炮S》第七话中,Level 5的超能力者Railgun(御坂美琴)得知了学园都市的“量产型能力者计划”,决定通过破坏研究所来阻止这个计划。为了在破坏研究所时不被监控装置发现,Railgun需要先关掉学园都市的部分监控装置。
学园都市的信息网络共有N个节点,这些节点用正整数1…N编号,它们之间通过N-1条地下光缆连接,保证任意两个节点间都是连通的。每条光缆上有一个监控装置,初始时有些监控装置是开着的,有些则是关闭的。 Railgun现在正在电话亭入侵学园都市的网络,她可以按照如下的方式操作监控装置:选择两个不同的网络节点a,b,将节点a到节点b的路径上的监控装置状态都取反(开着的装置关掉、关闭的装置打开)。注意这里的路径是指节点a,b间不经过重复节点的那一条唯一路径。
有些监控装置必须关掉后,才能保证Railgun不被发现,她希望经过若干次操作后这些装置处于关闭状态。你的任务就是计算最少需要多少次操作达到目的。
输入共N+2行,第一行包含一个正整数N
第二行包含一个长度为N-1的01字符串,第i个字符表示开始时第i条光缆上的监控装置是否开着,1表示开着,0表示关闭。
第三行包含一个长度为N-1的01字符串,第i个字符表示第i条光缆上的监控装置对于Railgun来说是否必须关闭,1表示必须关闭,0表示无需关闭。
下面N-1行,每行包含两个正整数a,b(1≤a,b≤N,a≠b)表示节点a和节点b间存在一条光缆。(N≤100,000)
输出共一行,包含一个非负整数,表示到达Railgun的目的所需的最少操作次数
5
1110
0111
1 2
1 3
2 4
2 5
1
上图中,对于Railgun来说必须关闭的监控装置被标为红色,每条道路旁边的01数字表示这条光缆上监控装置的状态。最优的方案是选择节点3和4,虚线包围的道路上的装置改变状态(全部从打开变成关闭)。
如果装置是关闭的又需要它关闭,则不需要操作…不进行连边
如果装置是开启的又需要它关闭,则连边,值为1
其他情况连边,值为0
循环遍历所有节点,如果没有被标记过就以该节点为根节点dfs,对遍历过的节点进行标记…
如果一个节点有偶数个子节点(n)路径需要操作,对答案的贡献就是n/2
如果一个节点有奇数个子节点(n)路径需要操作,对答案的贡献就是n/2,剩下的一条边延伸给父节点
贴个图
黑色是无关装置,红色是要操作的装置,绿色是操作的路径
链式前向星详解
#include
using namespace std;
const int maxn=1e5+5;
struct node{
int to,v,next;
}edge[maxn<<2];
int head[maxn],vis[maxn],cnt=0,ans=0;
char a[maxn],b[maxn];
void add(int a,int b,int c){//前向星存图
edge[++cnt].to=b;
edge[cnt].v=c;
edge[cnt].next=head[a];
head[a]=cnt;
}
int dfs(int now,int ju){//now当前节点,ju与父节点连边是否需要操作
vis[now]=1;
int num=0;
for(int i=head[now];i!=-1;i=edge[i].next){
int to=edge[i].to,v=edge[i].v;
if(vis[to])
continue;
if(dfs(to,v))
++num;
}
ans+=num/2;
return num%2==1||ju;
}
int main()
{
int n;
scanf("%d%s%s",&n,a+1,b+1);
memset(head,-1,sizeof(head));
for(int i=1;i<n;++i){
int from,to;
scanf("%d %d",&from,&to);
if(a[i]=='1'&&b[i]=='1')
add(from,to,1),add(to,from,1);
else if(a[i]=='1'&&b[i]=='0'){
add(from,to,0),add(to,from,0);
}else if(a[i]=='0'&&b[i]=='0'){
add(from,to,0),add(to,from,0);
}
}
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;++i){
if(vis[i]==0&&dfs(i,0)){
++ans;
}
}
printf("%d\n",ans);
return 0;
}
「十代目,在吗?」
「啊~,Reborn布置的数学题,完全不知道怎么做啊!」
「没关系,交给我吧!」
Reborn 给了一个数字 X ,27 可以给 N 个数字指定值,使得这 N 个数字进行或操作以后,再乘上任意一个数字的结果等于 X, 问有多少种指定方法。
「十代目,我已经算出来了,就这样这样这样…」
聪明的 59 已经解决了问题,那你呢?
第一行两个整数 X (1≤X≤109) 和 N (1≤N≤109)
打印一个整数,表示所求答案(结果对1000000007取模)。
2 2
6
有以下6种方法, 分别为 ( 第一个数字的值, 第二个数字的值, 乘数 ) :
(0, 2, 1)
(2, 2, 1)
(1, 0, 2)
(0, 1, 2)
(1, 1, 2)
题目大意:n个数字进行或操作乘某一个数字等于x,求这n个数字的不同组合数
也就是求n个数字或操作后成为x因子的组合数
我们就枚举x的因子i,求n个数或操作后等于i的不同组合,全部累加
或操作考虑二进制
如果i的二进制某一位为0那么这n个数中这一位不能有1 --只有一种情况
如果i的二进制某一位为1那么这n个数中这一位必须有1
可能有一个1(C(n,1))可能有两个1(C(n,2))…可能有n个1(C(n,n))
所以所有的可能为(C(n,1)+C(n,2)+C(n,3)+…C(n,n) )化简得2^n-1
用到的是一个组合数求和的公式
如果i的二进制有多个1,因为每个位的选择都是独立的,那就是多个2^n-1相乘
如果i的二进制有num个1,则n个数或操作后等于i的组合为
把x的所有因子的组合数相加就是答案
快速幂详解
#include
#define ll long long
using namespace std;
const int mod=1000000007;
ll pow_(ll a,ll b){//快速幂
ll ans=1;
while(b){
if(b&1)
ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
int main()
{
ll x,y,n,ans=0;
scanf("%lld %lld",&x,&n);
y=sqrt(x);
for(int i=1;i<=y;++i){
if(x%i==0){//i是因子
int j=x/i;//j也是因子
int now=i,num=0;
while(now){//求i的二进制形式1的个数
if(now&1)
++num;
now>>=1;
}
ans=(ans+pow_(pow_(2,n)-1,num))%mod;
if(i==j){//因子重复
continue;
}
now=j,num=0;
while(now){//求j的二进制形式1的个数
if(now&1)
++num;
now>>=1;
}
ans=(ans+pow_(pow_(2,n)-1,num))%mod;
}
}
printf("%lld\n",ans);
return 0;
}