题目描述
小蓝有很多数字卡片,每张卡片上都是数字 0 到 9。 小蓝准备用这些卡片来拼一些数,他想从 1 开始拼出正整数,每拼一个, 就保存起来,卡片就不能用来拼其它数了。 小蓝想知道自己能从 1 拼到多少。 例如,当小蓝有 30 张卡片,其中 0 到 9 各 3 张,则小蓝可以拼出 1 到 10, 但是拼 11 时卡片 1 已经只有一张了,不够拼出 11。 现在小蓝手里有 0 到 9 的卡片各 2021 张,共 20210 张,请问小蓝可以从 1 拼到多少?
#includeusing namespace std; //卡片 int num[10];//9种卡片的数量 bool check(int n){ while(n){ int i=n%10; if(num[i]>0) num[i]--; else return false; n/=10; } return true; } int main() { for(int i=0;i<10;i++) num[i]=2021; for(int k=1;;k++){ if(check(k)==false){ cout< 平面切分 题目描述
平面上有 N 条直线,其中第 i 条直线是 y = y=Ai*x+Bi。
请计算这些直线将平面分成了几个部分。
输入描述
第一行包含一个整数 N。
以下 N 行,每行包含两个整数 Ai,Bi。
其中,1≤N≤1000,−10^5≤Ai,Bi≤10^5。
输出描述
一个整数代表答案。
输入(三条直线相交于一点,不要看坐标轴)
3 1 1 2 2 3 3输出
6代码:
#include字串排序using namespace std; //平面切分 #include int n,ans=1; set > line;//用于直线去重 int calc(int a , int b){//计算新增一条直线区域增加数 set >node;//相交点坐标 for(auto i : line){//遍历容器中的所有元素 int A = i.first , B = i.second; if(A == a) continue ;//两直线平行,进入下次循环 double x = 1.0 * (B - b) / (a - A);//交点横坐标 double y = x * a + b; node.insert(make_pair(x , y)); } return node.size() + 1; } int main() { cin >> n; for(int i=1; i<=n; i ++) { int a, b; cin>>a>>b; if(line.find(make_pair(a,b))!=line.end()) continue;//如果输入的有重复 ans += calc(a, b); line.insert(make_pair(a, b)); } cout< 题目描述
小蓝最近学习了一些排序算法,其中冒泡排序让他印象深刻。
在冒泡排序中,每次只能交换相邻的两个元素。
小蓝发现,如果对一个字符串中的字符排序,只允许交换相邻的两个字符,在所有可能的排序方案中,冒泡排序的总交换次数是最少的。
例如,对于字符串 lan 排序,只需要 1 次交换。对于字符串 qiao 排序,总共需要 4 次交换。
小蓝的幸运数字是 V,他想找到一个只包含小写英文字母的字符串,对这个串中的字符进行冒泡排序,正好需要 V 次交换。请帮助小蓝找一个这样的字符串。如果可能找到多个,请告诉小蓝最短的那个。如果最短的仍然有多个,请告诉小蓝字典序最小的那个。请注意字符串中可以包含相同的字符。
输入描述
输入一行包含一个整数 V (1≤V≤10^4),为小蓝的幸运数字。
输出描述
输出一个字符串,为所求的答案。
示例 1
输入
4输出
bbaa示例 2
输入
100输出
jihgfeeddccbbaa交换次数=字符串中逆序对数
当给定字符串长度n,最大交换次数:即字母表前n个字母逆序所求次数
根据上面的公式,对输入的v,从len=1开始枚举,直至maxlen>=v,此时len为所求字符串长度。
已知长度求最小字典序
我们设最短长度为 len。若 maxlen=X,则必然可以通过改变字符顺序构造出长度为 len,逆序对个数为 X−1,X−2,⋯,0 的字符串。
于是我们可以从字符串第 1 个位置开始往后构造,具体为:
从小到大枚举字符 ch(ch∈[a∼z]),判断当前位置的字符为 ch 是否可以构造出长度为 len,且逆序对个数大于等于 V 的字符串。
我们设当前位置为 pos,那么 1∼(pos−1) 位置的字符肯定已经构造完成,那么当前位置选择字符 ch 可以新增的逆序对个数就为 1∼(pos−1) 中比 chch 大的字符的个数。对于 (pos+1)∼len 的位置,我们总共要添加 len−pos 个字符,且要保证这些字符能够产生最多的逆序对。我们可以暴力点一个一个插入。若插入了字符 x,则产生的逆序对个数 = 1∼pos 小于 x 的字符个数) +(len−pos−字符x 的数量)。每次我们选择插入能产生最多逆序对个数的字符,并统计插入后产生的总逆序对个数。
最后若第 pos 个位置的字符为 ch 可以构造出长度为 len,且逆序对个数大于等于 V 的字符串,我们就令第 pos 个位置的字符为 ch,继续构造第 pos+1 位置的字符;否则我们就判断第 pos 个位置的字符为 ch+1 是否可以构造出长度为 len,且逆序对个数大于等于 V 的字符串。
时间复杂度为 O(26len2)。(当 V=10000 时,len=145,所以复杂度完全是 ok 的)
代码:
#includeusing namespace std; #include int v;//交换次数 int len;//符合交换次数的字符串的最小长度 int now;//当前已构造好的一部分字符串的逆序对个数 int cnt[27];//当前位置后续部分包含字母a~z的个数 int sum[27];//当前构造的部分字符串包含字母a~z的个数 int get_max(int len){ return ((len-(len/26+1))*(len/26+1)*(len%26)+(26-len%26)*(len/26)*(len-len/26))/2; } //当前位置上放置的是第x个字母(即'a'+x-1),后面还有n个位置 bool check(int x,int n){ memset(cnt,0,sizeof(cnt)); int add1=0;//在当前位置放置x后新增的逆序对个数 int add2=0;//在当后续位置放置字母时最多新增的逆序对个数 //统计已经放置好的部分,比x大的字符个数,即add1 for(int j=26;j>=x+1;j--) add1+=sum[j]; sum[x]++ ;//在当前位置放置x,则x个数加1 for(int L=1;L<=n;L++){//考察当前位置后面共n个位置的第L个位置 int ma=-1;//在后续第L个位置上放字母新增的逆序对数最大值 int pos=0; int num=0;//已经放置的大于第j个字母的字符个数 //考察字母a~z,优先放更大的字母:越逆序,交换次数越多 for(int j=26;j>=1;j --){ if(L-1-cnt[j]+num>ma){//现在还没插入第j个字母 ma=L-1-cnt[j]+num; pos=j; } num+=sum[j]; } add2+=ma, cnt[pos]++; } if(now+add1+add2>=v){ now+=add1; return true; } else { sum[x]-- ; return false; } } int main() { string ans=""; cin>>v; //所求字符串长度 for(int i=1; ;i++){ if(get_max(i)>=v){ len=i; break ; } } //构造字典序最小的字符串 for(int i=1;i<=len;i++){ for(int j=1;j<=26;j++){//在位置i上试探性放置第j个字母 if(check(j,len-i)){//位置i上放置字母 ans+=char(j+'a'-1); break; } } } cout<



