3月20号将是我第一次外出比赛。 虽然是蓝桥杯。但是还是刷新题目下热热身
蓝桥杯--历年试题 【编程大题】
PREV-1 核桃的数量 最小公倍数
小张是软件项目经理,他带领3个开发组。工期紧,今天都在加班呢。为鼓舞士气,小张打算给每个组发一袋核桃(据传言能补脑)。他的要求是:
1. 各组的核桃数量必须相同
2. 各组内必须能平分核桃(当然是不能打碎的)
3. 尽量提供满足1,2条件的最小数量(节约闹革命嘛)
思路:太水了。 就是找一个数并且能同时整除abc,最小公倍数
#include
#include
#include
using namespace std;
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b)
{
return a/gcd(a,b)*b;
}
int main()
{
int num,ans=1;
for(int i=0;i<3;i++)
{
scanf("%d",&num);
ans=lcm(ans,num);
}
printf("%d\n",ans);
return 0;
}
PREV-2 打印十字图 文字图
[暂时做不出来。。。讨厌这种画图题。ORZ。]
PREV-3 带分数 搜索
100 可以表示为带分数的形式:100 = 3 + 69258 / 714。
还可以表示为:100 = 82 + 3546 / 197。
注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。
类似这样的带分数,100 有 11 种表示法。
从标准输入读入一个正整数N (N<1000*1000)
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。
注意:不要求输出每个表示,只统计有多少表示法!
思路:将式子看出N=A+B/C的形式,观测到数字1~9分别出现且只出现一次这一条件就可以想到枚举全排列。然后将全排列分成3个部分。分别
分别是A B C三个部分,然后判断(N-A)*C==B,注意ABC都是至少需要一个数来构成。全排列的9的数字中[记为1-9],A只能从1枚举到7
B只能从2枚举到8,C只能从3枚举到9. 简单优化一下:当A组成的数字大于N时就不应该继续枚举了。因为永远得不到结果。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=1000*1000+5;
int num[10],vis[MAXN];
int get_num(int st,int ed)
{//全排列的st到ed组成的数字
int m=0;
for(int i=st;i<=ed;i++)
{
m=m*10+num[i];
}
return m;
}
int main()
{
int n,ans;
while(scanf("%d",&n)!=EOF)
{
ans=0;
int A,B,C;
for(int i=0;i<9;i++)
{
num[i]=i+1;
}
do{//枚举全排列
for(int i=0;i<9;i++)
{
A=get_num(0,i);
if(A>=n)
{//优化。
break;
}
for(int j=i+1;j<8;j++)
{
B=get_num(i+1,j);
C=get_num(j+1,8);
if(B==(n-A)*C)
{
ans++;
}
}
}
}while(next_permutation(num,num+9));
printf("%d\n",ans);
}
return 0;
}
如下图所示,3 x 3 的格子中填写了一些整数。
我们沿着图中的星号线剪开,得到两个部分,每个部分的数字和都是60。
本题的要求就是请你编程判定:对给定的m x n 的格子中的整数,是否可以分割为两个部分,使得这两个区域的数字和相等。
如果存在多种解答,请输出包含左上角格子的那个区域包含的格子的最小数目。
如果无法分割,则输出 0。
程序先读入两个整数 m n 用空格分割 (m,n<10)。
表示表格的宽度和高度。
接下来是n行,每行m个正整数,用空格分开。每个整数不大于10000。
思路:因为记录的是最上角的的分割区可能包含的最小的格子数目,所以很容易就会想到从左上角开始深搜。很可惜。这样做案例2是无法得到正确答案的。因为一般的深搜只能按一个方向[上下左右]一直搜索,而不能想案例2那样"拐弯"搜索,但是不管怎么搜索,正确结果的搜索区域即不管怎么"拐弯",得到的区域都是连续的,那么我们可以从所有位置开始搜索,当满足条件并且同时搜索到左上角的位置时就可以记录了。虽然是100分过,但是我还是觉得这题测试数据不够严谨,即使暴力所有点开始dfs搜索都还是可以找反例。即结果区域呈"十字架/T字形"的话,暴力起点搜还是不会得到正确结果的。不过没想到更好的办法了。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=15;
int num[MAXN][MAXN],n,m,s,ans;
int vis[MAXN][MAXN],dist[4][2]={0,1,0,-1,1,0,-1,0};
//标记数组[回溯用],方向向量
bool check(int x,int y)
{//是否越界
if(x>=0&&x=0&&ys||total>=ans)
{//剪枝:当前数已经大于整体的一般,或者
return;//包括的区域数字已经大于当前最优解
}
if(val==s&&vis[0][0])
{//到最终状态并且搜索到左上角
ans=min(ans,total);
}
for(int i=0;i<4;i++)
{
int nextx=x+dist[i][0];
int nexty=y+dist[i][1];
if(check(nextx,nexty)&&!vis[nextx][nexty])
{
vis[nextx][nexty]=1;
dfs(nextx,nexty,val+num[nextx][nexty],total+1);
vis[nextx][nexty]=0;
}
}
}
int main()
{
while(scanf("%d%d",&m,&n)!=EOF)
{
ans=105;
int sum=0;
memset(vis,0,sizeof(vis));
for(int i=0;i
某涉密单位下发了某种票据,并要在年终全部收回。
每张票据有唯一的ID号。全年所有票据的ID号是连续的,但ID的开始数码是随机选定的。
因为工作人员疏忽,在录入ID号的时候发生了一处错误,造成了某个ID断号,另外一个ID重号。
你的任务是通过编程,找出断号的ID和重号的ID。
假设断号不可能发生在最大和最小号。
要求程序首先输入一个整数N(N<100)表示后面数据行数。
接着读入N行数据。
每行数据长度不等,是用空格分开的若干个(不大于100个)正整数(不大于100000),请注意行内和行末可能有多余的空格,你的程序需要能处理这些空格。
每个整数代表一个ID号。
要求程序输出1行,含两个整数m n,用空格分隔。
其中,m表示断号ID,n表示重号ID
思路:水题。按照题目意思模拟即可,主要考察字符串的处理[字符串->数字的转换]。断点和重复只有一个。记录数字的最大值和最小值。开个数组记录那些数字已经出现过,然后暴力找最小值到最大值那个没出现过。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=100000+5;
const int MAXL=20000000;
int num[MAXN],minnum,maxnum;
char str[MAXL];
int main()
{
int n,repeat,vacancy;
while(scanf("%d",&n)!=EOF)
{
memset(num,0,sizeof(num));
minnum=MAXN+1;
maxnum=-1;
getchar();
for(int i=0;i
PREV-6 翻硬币 贪心
小明正在玩一个“翻硬币”的游戏。
桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。
比如,可能情形是:**oo***oooo
如果同时翻转左边的两个硬币,则变为:oooo***oooo
现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?
我们约定:把翻动相邻的两个硬币叫做一步操作,那么要求:
两行等长的字符串,分别表示初始状态和要达到的目标状态。每行的长度<1000
一个整数,表示最小操作步数。
思路:因为只能一次翻2个,所以想匹配最左边,如果匹配则继续匹配下一个位置,否则就必须翻这个位置的硬币[同时改变他下一个位置的硬币,因为一翻就会改动2个位置],模拟就好了
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXL=1000+5;
char st[MAXL],ed[MAXL];
char chang(char a)
{
return a=='*'?'o':'*';
}
int main()
{
while(scanf("%s",st)!=EOF)
{
scanf("%s",ed);
int step=0,len=strlen(st);
for(int i=0;i
小明这些天一直在思考这样一个奇怪而有趣的问题:
在1~N的某个全排列中有多少个连号区间呢?这里所说的连号区间的定义是:
如果区间[L, R] 里的所有元素(即此排列的第L个到第R个元素)递增排序后能得到一个长度为R-L+1的“连续”数列,则称这个区间连号区间。
当N很小的时候,小明可以很快地算出答案,但是当N变大的时候,问题就不是那么简单了,现在小明需要你的帮助。
第一行是一个正整数N (1 <= N <= 50000), 表示全排列的规模。
第二行是N个不同的数字Pi(1 <= Pi <= N), 表示这N个数字的某一全排列。
输出一个整数,表示不同连号区间的数目。
思路:起初我是暴力的,没暴力每个起点和终点。然后将起点和终点的数排序。记L=起点位置,R=终点位置。那么用个vector把这个区间的数都push进去,然后排序一遍,然后最大值-最小值+1=size()的话就满足条件。然后这样做只能得60分。因为是O(n*n*nlogn)。但是想一下。只会出现1-N的数字。所以只需要继续L,R区域里的最大值和最小值。如果区域最大值-最小值=区域个数,那么就一定满足要求。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=50000+5;
int num[MAXN];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
int ans=0;
for(int i=0;i
小明开了一家糖果店。他别出心裁:把水果糖包成4颗一包和7颗一包的两种。糖果不能拆包卖。
小朋友来买糖的时候,他就用这两种包装来组合。当然有些糖果数目是无法组合出来的,比如要买 10 颗糖。
你可以用计算机测试一下,在这种包装情况下,最大不能买到的数量是17。大于17的任何数字都可以用4和7组合出来。
本题的要求就是在已知两个包装的数量时,求最大不能组合出的数字。
两个正整数,表示每种包装中糖的颗数(都不多于1000)
一个正整数,表示最大不能买到的糖数
思路:估算结果范围+暴力DP。。。因为没想到其他比较好的办法,所以就估算结果会在1000*1000内。假设输入的数为a,b 先dp筛掉a的倍数。然后i从b-1000*1000筛选b的倍数的i和能够凑成i-b的i,看代码吧。
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=1000*1000+1;
bool dp[MAXN];
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==1||m==1)
{//不知道会不会有1这样的数据出现,因为1是能组成任何大于1数
printf("0\n");
continue;
}
memset(dp,false,sizeof(dp));
if(n>m)
{
swap(n,m);
}
for(int i=n;i
PREV-9 大臣的旅费 深度优先遍历
很久以前,T王国空前繁荣。为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。
为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。
J是T国重要大臣,他巡查于各大城市之间,体察民情。所以,从一个城市马不停蹄地到另一个城市成了J最常做的事情。他有一个钱袋,用于存放往来城市间的路费。
聪明的J发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第x千米到第x+1千米这一千米中(x是整数),他花费的路费是x+10这么多。也就是说走1千米花费11,走2千米要花费23。
J大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?
输入的第一行包含一个整数n,表示包括首都在内的T王国的城市数
城市从1开始依次编号,1号城市为首都。
接下来n-1行,描述T国的高速路(T国的高速路一定是n-1条)
每行三个整数Pi, Qi, Di,表示城市Pi和城市Qi之间有一条高速路,长度为Di千米。
输出一个整数,表示大臣J最多花费的路费是多少。
大臣J从城市4到城市5要花费135的路费。
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=1000005;
long long int dist[MAXN];
struct Node
{
int to,val;
Node(int a,int b):to(a),val(b){}
};
vectorG[MAXN];
void init()
{
dist[0]=0;
int len=11;
for(int i=1;i h1) h2 = h1, h1 = h;
else if (h > h2) h2 = h;
}
}
diameter = max(diameter, h1 + h2);
return h1;
}
int main()
{
int n;
init();
while(scanf("%d",&n)!=EOF)
{
for(int i=1;i<=n;i++)
{
G[i].clear();
}
int u,v,w;
for(int i=1;i
PREV-10 幸运数 堆
思路:无脑按照题目要求把表打出来。1000*150这个范围内还是很快可以把表打出来的。但是极限数据就非常慢。 题目要求的1000*1000卡了好几秒才能读数据。但是估计数据真心水,1000*150的表都能100分过。注意下不能包括n,m。 暂时没想到什么能解决1000*1000的高效办法。有人说可以用打素数表的方法,但是我是没想出来。这个和素数关系好像不大,因为要筛的是按位置来筛,而不是按照数字本身来特点来筛。 无力,小数据水过。
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=1000*150;//小数据。
vectorlucky;//幸运数表
void init()
{
for(int i=1;i=lucky.size())
{//剪枝,当前数已经大于表的大小,后面的数都无法筛
break;
}
for(int j=1;j*num-j0)//因为不能包括m所以减掉
{
R--;//因为可能r--后出现l>r的情况
}
printf("%d\n",max(0,R-L+1));//所以输出时判断结果不为负数
//s2=clock();
//printf("%f ms\n", (double)(s2 - s1));
}
return 0;
}
PREV-12 危险系数 割点
抗日战争时期,冀中平原的地道战曾发挥重要作用。
地道的多个站点间有通道连接,形成了庞大的网络。但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系。
我们来定义一个危险系数DF(x,y):
对于两个站点x和y (x != y), 如果能找到一个站点z,当z被敌人破坏后,x和y不连通,那么我们称z为关于x,y的关键点。相应的,对于任意一对站点x和y,危险系数DF(x,y)就表示为这两点之间的关键点个数。
本题的任务是:已知网络结构,求两站点之间的危险系数。
输入数据第一行包含2个整数n(2 <= n <= 1000), m(0 <= m <= 2000),分别代表站点数,通道数;
接下来m行,每行两个整数 u,v (1 <= u, v <= n; u != v)代表一条通道;
最后1行,两个数u,v,代表询问两点之间的危险系数DF(u, v)。
思路:模拟暴力删除点[除了起点和终点],然后跑一次DFS看看连不连通。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=1000+5;
#define INF 0x3f3f3f3f
vectorG[MAXN];
int ans,st,ed,vis[MAXN];
bool flag;//判断是否连通
void dfs(int u)
{
if(u==ed||flag)
{
flag=true;
return;
}
for(int i=0;i
PREV-13 网络寻路 构图
X 国的一个网络使用若干条线路连接若干个节点。节点间的通信是双向的。某重要数据包,为了安全起见,必须恰好被转发两次到达目的地。该包可能在任意一个节点产生,我们需要知道该网络中一共有多少种不同的转发路径。
源地址和目标地址可以相同,但中间节点必须不同。
如下图所示的网络。
1 -> 2 -> 3 -> 1 是允许的
1 -> 2 -> 1 -> 2 或者 1 -> 2 -> 3 -> 2 都是非法的。
输入数据的第一行为两个整数N M,分别表示节点个数和连接线路的条数(1<=N<=10000; 0<=M<=100000)。
接下去有M行,每行为两个整数 u 和 v,表示节点u 和 v 联通(1<=u,v<=N , u!=v)。
输入数据保证任意两点最多只有一条边连接,并且没有自己连自己的边,即不存在重边和自环。
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=10000+5;
#define INF 0x3f3f3f3f
vectorG[MAXN];
int ans;
void dfs(int u,int fa,int len)
{//u为当前节点,fa为上一个结点,len为长度
if(len==3)
{
ans++;
return;
}
for(int i=0;i0)
{
dfs(i,-1,0);
}
}
printf("%d\n",ans);
}
return 0;
}
PREV-19 九宫重排 搜索
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=1000+5;
#define INF 0x3f3f3f3f
#define MOD 1000000007
setvis;//判重集合
int dist[4][2]={0,1,0,-1,1,0,-1,0};//方向向量
bool check(int x,int y)//是否越界
{
if(x>=0&&x<3&&y>=0&&y<3)
{
return true;
}
return false;
}
void init()//预处理每个位置能移动的下一个位置
{
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
for(int k=0;k<4;k++)
{
int x=i+dist[k][0];
int y=j+dist[k][1];
if(check(x,y))
{
G[i*3+j].push_back(x*3+y);
}
}
}
}
}
struct Node
{//状态结果,保存当前的字符串[八数码的状态]和步数
string str;
int step;
};
int bfs(string st,string ed)
{
queueQ;
Node start;
start.str=st;
start.step=0;
Q.push(start);
vis.insert(st);
while(!Q.empty())
{
Node top,next;
top=Q.front();
Q.pop();
if(top.str==ed)
{
return top.step;
}
int index;
for(int i=0;i>st;
cin>>ed;
cout<
PREV-21 回文数字
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=10000+5;
vectorfive;
vectorsix;
void init()
{
int num=0;
for(int i=1;i<10;i++)
{
for(int j=0;j<10;j++)
{
for(int k=0;k<10;k++)
{
num=i*10000+j*1000+k*100+j*10+i;
five.push_back(num);
num=i*100000+j*10000+k*1000+k*100+j*10+i;
six.push_back(num);
}
}
}
sort(five.begin(),five.end());
sort(six.begin(),six.end());
}
int sum_of_num(int n)
{
int s=0;
while(n)
{
s+=n%10;
n/=10;
}
return s;
}
int main()
{
init();
int n;
while(scanf("%d",&n)!=EOF)
{
int cnt=0;
for(int i=0;i
PREV-22 国王的烦恼
思路:起初以为要各点都可达,发现不是,只要删掉的边的两个城市不可达就会发生暴动,以为是按时间顺序删除边的。那么我们可以反过来,起初全部边都有->按时间顺序的删除边==起初全部边都没有->按逆序顺序的添加边。 就是逆序使用并查集。注意下可能一天添加多条边但是别重复计算了。
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=10000+5;
const int MAXM=100000+5;
#define INF 0x3f3f3f3f
struct UF
{
private:
int count;//不相交的集合数目
int size[MAXN];//结点所在集合的大小,用于优化子树合并后的高度
int path[MAXN];//对应根结点。
public:
void Init(int n)
{
for (int i = 1; i <= n; i++)
{
path[i] = i;
size[i] = 1;
}
count=n;
}
bool connected(int p, int q)//是否在一个集合
{
return Find(p) == Find(q);
}
int Count()
{
return count;
}
int Find(int x)//找根结点 O(lgN)
{
while (x != path[x])
{
// 将p节点的父节点设置为它的爷爷节点
path[x] = path[path[x]];
x = path[x];
}
return x;
}
bool Union(int p, int q)//如果p,q本来就在同一个连通分量返回true;
{//合并q,p集合
int i = Find(p);
int j = Find(q);
if (i == j)
{
return true;
}
// 将小树作为大树的子树
if (size[i] < size[j])
{
path[i] = j;
size[j] += size[i];
}
else
{
path[j] = i;
size[i] += size[j];
}
return false;
}
};
struct Edge
{
int u,v,t;
};
Edge e[MAXM];
bool cmp(Edge a,Edge b)
{
return a.t>b.t;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=0;i
PREV-23 数字游戏
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long int LL;
LL n,k,t;
LL sum(LL a0,LL an,LL cnt)
{//求等差数列的和(a0:第一项 an:最后一项 cnt:项数
if(cnt%2)
{//注意乘法取模: (a*b)%c== (a%c * b%c)%c
return ((((a0+an)/2)%k)*(cnt%k))%k;
}
else
{
return (((cnt/2)%k)*((a0+an)%k))%k;
}
}
int main()
{
while(scanf("%lld%lld%lld",&n,&k,&t)!=EOF)
{
LL ans=1,st=1,ed=n;
for(int i=1;i
PREV-24 邮局
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=10000+5;
#define INF 0x3f3f3f3f
struct Point
{
double x,y;
}peo[55],post[35];
double dist(Point a,Point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double mindist,dis[35][55];
//当前最优距离和,初始化每个人到每个邮局的距离
int n,m,k;
int post_num[15],ans_post_num[15];
//中间结果,最终结果
void dfs(int index,int cnt)
{//当前考虑邮局index,已经选了cnt个邮局
if(cnt==k)//已经选了k个邮局
{
double sum_dist=0;
for(int i=0;i
PREV-26 最大子阵
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=500+5;
int g[MAXN][MAXN],d[MAXN][MAXN];
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
int maxval=-5005;
memset(d,0,sizeof(d));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&g[i][j]);
d[i][j]=d[i-1][j]+g[i][j];//保存列的前缀和。
maxval=max(maxval,g[i][j]);
}
}
/*for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cout<
PREV-27 蚂蚁感冒
思路:先分别存两边的蚂蚁的位置,两个蚂蚁相遇且同时调头方向行走可以看出两个蚂蚁擦肩而过继续按原来的方向行走,然后分类讨论,如果感冒的蚂蚁是朝右走的,那么朝左走并且位置大于感冒的蚂蚁的话都会被感染[然后因为方向相反并且位置大于它,所以肯定会相遇],然后,这些新感染感冒的蚂蚁[往左走的]当中位置最小的,将变成"新的起初感染的蚂蚁",然后找往右走的蚂蚁中,位置小于它的,这些也会被感染感冒。 同理可得开始感冒蚂蚁朝左。注意这些蚂蚁别重复计算了。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
vectorLeft;
vectorRight;
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
int fst=0;
Left.clear(),Right.clear();
for(int i=0;i0)
{
Right.push_back(val);
}
else
{
Left.push_back(val);
}
}
int ans=1;//因为有一只蚂蚁已经感冒了,即第一只
if(fst>0)//分类讨论 ,大于0即感冒的蚂蚁朝右
{//那么往左走的位置大于它的蚂蚁都会被感染
int tmp=1000;
for(int i=0;i=fst)
{
ans++;
tmp=min(tmp,Left[i]*-1);
}
}
if(tmp!=1000)//如果有往走的蚂蚁被感染到。
{//那么这只蚂蚁就会感染往右走并且位置小于它的其他蚂蚁
for(int i=0;i=tmp&&Left[i]!=fst)
{
ans++;
}
}
}
printf("%d\n",ans);
}
return 0;
}
PREV-28 地宫取宝
记忆化思维DP,不太会写,看了别人的报告才写出来。 详细点这里 注意几个点,初始最大值应该是-1因为可能存在价值为0的位置。 所以数字需要maxv+1,
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=55;
const int MAXK=15;
const int MAXV=15;
const int MOD=1000000007;
int v[MAXN][MAXN],dp[MAXN][MAXN][MAXK][MAXV];
int n,m,k;
int dfs(int x,int y,int kk,int maxv)
{//当前位置(x,y),手上有kk件宝物,这些宝物的最大值
if(dp[x][y][kk][maxv+1]!=-1)
{
return dp[x][y][kk][maxv+1];
}
if(x==n-1&&y==m-1)
{//边界
if(kk==k)
{
return dp[x][y][kk][maxv+1]=1;
}
else if(kk==k-1&&v[x][y]>maxv)
{
return dp[x][y][kk][maxv+1]=1;
}
else
{
return dp[x][y][kk][maxv+1]=0;
}
}
long long int cnt=0;
if(ymaxv)//拿
{
cnt=(cnt+dfs(x,y+1,kk+1,v[x][y]))%MOD;
}
}
if(xmaxv)//拿
{
cnt=(cnt+dfs(x+1,y,kk+1,v[x][y]))%MOD;
}
}
return dp[x][y][kk][maxv+1]=cnt%MOD;
}
int main()
{
while(scanf("%d%d%d",&n,&m,&k)!=EOF)
{
memset(dp,-1,sizeof(dp));
for(int i=0;i
PREV-31 小朋友排队
#include
#include
#include
#include
#include
using namespace std;
#define Lowbit(x)(x&(-x))
const int MAXN=1000000+5;
typedef long long int LL;
LL c[MAXN],n,h[MAXN],l_max[MAXN],r_min[MAXN],cost[MAXN];
//c:树状数组,n:人数,h:对应身高,l_max[i]:站在i左边并且身边比i高的人数
//r_min[i]:站在i右边并且身高比i低的人数,cost:愤怒值
LL sum(int x)
{
LL s=0;
while(x>0)
{
s+=c[x];
x-=Lowbit(x);
}
return s;
}
void updata(int x,LL val)
{
if(x==0)
{
return;
}
while(x<=MAXN)
{
c[x]+=val;
x+=Lowbit(x);
}
}
void init()
{//初始化愤怒值。
cost[0]=0;
for(int i=1;i<=100000;i++)
{
cost[i]=cost[i-1]+i;
}
}
int main()
{
while(scanf("%lld",&n)!=EOF)
{
init();
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)
{
scanf("%d",&h[i]);
h[i]++;//防止出现h[i]=0的情况。因为树状数组不能有下标0的项
}
LL ans=0;
for(int i=n;i>=1;i--)
{//逆序的求右边比它低的人数[逆序数]
r_min[i]=sum(h[i]-1);
updata(h[i],1);
}
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)
{//顺序的求左边比它高的人数
l_max[i]=sum((MAXN-1))-sum(h[i]);
updata(h[i],1);
}
for(int i=1;i<=n;i++)
{//计算总愤怒值
ans+=cost[l_max[i]+r_min[i]];
}
printf("%lld\n",ans);
}
return 0;
}
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=100+5;
int val[MAXN],tmp[MAXN];
//val:每个人的糖果数, tmp:每个人需要分给左边的人的糖果数
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
for(int i=0;i
PREV-33 兰顿蚂蚁
思路:按照题意模拟吧。 好像不需要考虑会不会越界的问题。 而且题目也没说越界要怎么处理。。
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=100+5;
int g[MAXN][MAXN],n,m;
struct Node
{
int x,y,step;
char s;
Node(int a=0,int b=0,char c=0,int d=0):x(a),y(b),s(c),step(d){}
};
Node move(Node p,int temp)
{//按照题目移动,并返回移动后的状态[位置,步数,朝向]
Node next;
next.step=p.step-1;
if(temp==1)//黑格
{
if(p.s=='U')
{
next.x=p.x; next.y=p.y+1; next.s='R';
}
else if(p.s=='D')
{
next.x=p.x; next.y=p.y-1; next.s='L';
}
else if(p.s=='L')
{
next.x=p.x-1;next.y=p.y;next.s='U';
}
else
{
next.x=p.x+1;next.y=p.y;next.s='D';
}
g[p.x][p.y]=0;
}
else
{
if(p.s=='U')
{
next.x=p.x; next.y=p.y-1; next.s='L';
}
else if(p.s=='D')
{
next.x=p.x; next.y=p.y+1; next.s='R';
}
else if(p.s=='L')
{
next.x=p.x+1;next.y=p.y;next.s='D';
}
else
{
next.x=p.x-1;next.y=p.y;next.s='U';
}
g[p.x][p.y]=1;
}
return next;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=0;i