附上这些题目的链接
题目描述:
有12个数字,分别是1~12。
计算并输出这12个数字的全排列的种类数。
解题思路:
由题意知:题目实际要求1~12全排列的种类数。
由简单的数学知识我们知道:1~12的全排列的种类数为 12 !
package content;/**
* Copyright (C), 2019-2021
* author candy_chen
* date 2021/3/1 17:10
*
* @Classname test
* Description: 算法竞赛:排列巨人
*/
/**
*
*/
public class Tester_01{
public static void main(String[] args){
int n = 12;
int count = n * f(n - 1);
System.out.print("对于12名巨人的排列方式有" + count +"种:" );
int[] buf = {
1,2,3,4,5,6,7,8,9,10,11,12};
prem(buf,0,buf.length - 1);
return;
}
private static void prem(int[] buf, int start, int end) {
int count = 12;
if(start==end){
//输出排列好的数组
System.out.print("{");
for(int c:buf){
System.out.print(c);
if (count > 1){
System.out.print(",");
count--;
}
}
System.out.print("}");
System.out.print("、");
}else{
for(int i=start;i<=end;i++){
int temp=buf[start];//前后元素交换
buf[start]=buf[i];
buf[i]=temp;
prem(buf,start+1,end);//递归交换后面的元素
temp=buf[start];
buf[start]=buf[i];
buf[i]=temp;
}
}
}
private static int f(int i) {
int count = 1;
if (i > 0){
count = i * f(i - 1) ;
}
return count;
}
}
数据范围很小,可以O(S*T)枚举每一个位置的棋子。
接着判断这个棋子是否是横着、竖着、斜着(左上到右下、右上到左下两个方向)能够构成三连子。
如果可以,那么这个格子的棋子就是答案,直接输出即可。
全部棋子枚举完没有找到三连子,意味着平局,输出ADPC! 注意叹号是英文叹号
mp[i][j]为第i行第j列的格子上的棋子或无人下棋
第i行为:从上到下的第i行
第j列为:从左到右的第j列
在枚举过程中,设当前棋子为mp[i][j]
则左边挨着的棋子所在的列即为mp[i][j-1]、右边为mp[i][j+1]
判断三者是否相等即可完成横着方向的判断。
之后在竖、斜方向的判断仍然同理。
import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.io.Closeable;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.io.InputStream;
import java.util.Scanner;
//判断是否连成三子棋,可以枚举棋盘上每一个位置的棋子。
//接着判断这个棋子是否是横着、竖着、斜着(左上到右下、右上到左下两个方向)能够构成三连子,
//如果可以,那么这个格子的棋子就是答案,直接输出即可。
//全部棋子枚举完没有找到三连子,意味着平局,输出ADPC! 注意叹号是英文叹号
//
public class Main {
public static void main(String [] args) {
Scanner in = new Scanner(System.in);
int s = in.nextInt();
int t = in.nextInt();// 输入棋盘的行和列
char mp[][] = new char[33][33];
//mp[i][j]为第i行第j列的格子上的棋子或无人下棋
//第i行为:从上到下的第i行
//第j列为:从左到右的第j列
String str;
for(int i = 1; i <= s; i++) {
str = in.next();
for(int j = 1; j <= t; j++) {
mp[i][j] = str.charAt(j - 1);
}
}
for(int i = 1; i <= s; i++) {
for(int j = 1; j <= t; j++) {
//判断当前棋子为中心的横着三个棋子是否构成三子棋,即当前棋子和它左右两棋子是否相同
if(mp[i][j-1]==mp[i][j]&&mp[i][j]==mp[i]j+1]&&mp[i][j]!='.'){
System.out.println(mp[i][j]);//此时连成三子棋,输出此位置字符即为答案
return ;//return 0执行,程序结束。
}
//判断当前棋子为中心的竖着三个棋子是否构成三子棋,即当前棋子和它上下两棋子是否相同
if(mp[i-1][j]==mp[i][j]&&mp[i][j]==mp[i+1][j]&&mp[i][j]!='.'){
System.out.println(mp[i][j]);//此时连成三子棋,输出此位置字符即为答案
return ;//return 0执行,程序结束。
}
//判断当前棋子为中心的斜着(左上到右下)三个棋子是否构成三子棋,即当前棋子和它左上、右下两棋子是否相同
if(mp[i-1][j-1]==mp[i][j]&&mp[i][j]==mp[i+1][j+1]&&mp[i][j]!='.'){
System.out.println(mp[i][j]);//此时连成三子棋,输出此位置字符即为答案
return ;//return 0执行,程序结束。
}
//判断当前棋子为中心的斜着(右上到左下)三个棋子是否构成三子棋,即当前棋子和它右上、左下两棋子是否相同
if(mp[i-1][j+1]==mp[i][j]&&mp[i][j]==mp[i+1][j-1]&&mp[i][j]!='.'){
System.out.println(mp[i][j]);//此时连成三子棋,输出此位置字符即为答案
return ;//return 0执行,程序结束。
}
}
}
System.out.println("ADPC!");//如果有胜者前面直接输出并return 0了,因此这里直接输出即可
return ;
}
}
题目描述:
N个物品,第i个物品体积为,价格为。
M个背包,每个背包只能装一个物品,第i个背包的容积为
问如何购买物品使得背包中的价格总和最大。
解题思路:
一眼看过去,似乎与背包问题有关。
但题意清楚之后,是一个很显然的贪心,因为每个背包最多只能装一个物品。
且如果一个小体积的背包x能够装下一个物品i,那么其他比此背包大的背包也能装下物品i
,但因为其他背包体积更大,所以可以有更多的选择:因为可能存在某些体积恰好大于x的
物品j,且其价值更大。
所以贪心思路很容易得到:
从最小的背包依次枚举,对于当前背包x:在能放入背包的物品中选择价格最大的放入其中
,并计入答案即可。
and:
首先,将背包升序排序,将物品按照体积升序排序。
我们可以创建一个优先队列(大根堆),里面存放物品的价格。
每枚举到一个背包,就加入一些新的物品,这些物品是能够装下当前背包,且不能装下之
前枚举的较小体积背包的。
题解
在这里插入代码片
import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.io.Closeable;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.io.InputStream;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
/***********************************
观察题目样例给出的高为5层的塔,可以得出以下几个规律
对于一个高为n层的塔而言,首先设最上面一层(顶层)为第一层。
1. 对于第i层而言,其字符的排列规律为:大写字母表中从第1个字符(A)~第i个字符,后又倒序从第i-1个字符~第1个字符(A)。
2. 第1~n-1层每层前都有空格,具体而言,对于第i行,字符前面的空格个数为n-i个。
找出以上规律后,我们就可以根据这些规律构造出答案:层高26的塔。
TIPS:
大写字母'A'为大写字母表第一个字符
对于大写字母表中第i个字符,可以使用'A'+i-1得到。
例如:第5个字符为'E',亦即为:'A'+5-1
***********************************/
char c1;
int n=26;//设定塔的层数为26
for(int i=1;i<=n;i++){
//对塔每一层按照规律进行构造。
//首先进行输出空格的操作:对于第i行,字符前面的空格个数为n-i个。
for(int j = 1;j<=n-i;j++) {
System.out.print(" ");
}
for(int j=1;j<=i;j++){
//按照规律1,输出第1~第i个大写字母。
c1=(char) (j+'A'-1);//第j个大写字母为'A'+j-1
System.out.print(c1);//输出第j个大写字母
}
for(int j=i-1;j>=1;j--){
//按照规律1,输出第i-1~第1个大写字母,注意是倒序
c1=(char) (j+'A'-1);
System.out.print(c1);
}
System.out.println();//第i行输出结束,进行换行。
}
}
}
按照加密思路进行解密即可
遇到l、i、n、k、e时,其后面必是跟着bt和这个字母本身。
所以可以读取字符串后,for循环按顺序枚举并输出每个字母,但遇到l、i、n、k、e时不要
输出其后面的三个字母即可。
另一种做法:
使用getchar()按字母读取字符串,每读取到一个字母时就将其输出。
但遇到l、i、n、k、e时,使用三次getchar()读取后面加密多出来的字符,且不
输出即可跳过。
public class test_02 {
public static void main(String[] args) {
String s = "ibti lbtlovebte lbtlibtinbtnkbtkebtezbas jebte dosadnbtna ovakbtkebtemibtijaxaszxdbtddbtddbtddbtddbtddbtd";
int n = s.length();
for(int i = 0; i < n; i++) {
System.out.print(s.charAt(i));
if(s.charAt(i)=='l' || s.charAt(i)=='i' || s.charAt(i)=='n' || s.charAt(i)=='k' || s.charAt(i)=='e') i += 3;
}
}
}
package org.example;/**
* Copyright (C), 2019-2021
* author candy_chen
* date 2021/3/13 14:52
*
* @Classname Tester_02
* Description: 测试
*/
import java.util.*;
/**
*
*/
public class Main {
public static List<List<Integer>> res = new ArrayList<>();
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
int l = sc.nextInt();
int r = sc.nextInt();
int[] nums = new int[r];
// 定义全局变量保存结果
StringBuffer sb = new StringBuffer();
//1既不是质数也不是和数,使用i从2开始。
for (int i = 2; i <= r; i++) {
boolean flag = true;
for (int j = 2; j < i; j++) {
if (i % j == 0) {
flag = false;
break;//到了1000内的质数,就跳出循环,并输出结果。
}
}
if (flag) {
nums[i] = i;
int k = nums.length;
res.add(Collections.singletonList(nums[i]));
//sb.append(combine(nums[i],k));
//还有bug。。。。。。..。。
System.out.println(sb.append(res.toString()).toString());
}
}
}
//全排列问题
public static List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
if (k <= 0 || n < k) {
return res;
}
// 从 1 开始是题目的设定
Deque<Integer> path = new ArrayDeque<>();
dfs(n, k, 1, path, res);
return res;
}
private static void dfs(int n, int k, int begin, Deque<Integer> path, List<List<Integer>> res) {
// 递归终止条件是:path 的长度等于 k
if (path.size() == k) {
res.add(new ArrayList<>(path));
return;
}
// 遍历可能的搜索起点
for (int i = begin; i <= n; i++) {
// 向路径变量里添加一个数
path.addLast(i);
// 下一轮搜索,设置的搜索起点要加 1,因为组合数理不允许出现重复的元素
dfs(n, k, i + 1, path, res);
// 重点理解这里:深度优先遍历有回头的过程,因此递归之前做了什么,递归之后需要做相同操作的逆向操作
path.removeLast();
}
}
}
package content;/**
* Copyright (C), 2019-2021
* author candy_chen
* date 2021/3/14 21:11
*
* @Classname test_10
* Description: 测试
*/
import java.util.Scanner;
/**
*
*/
public class test_05 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String V = sc.nextLine();
int k = Integer.parseInt(V);
String s = sc.nextLine();
long ans = 0, cnt1 = 0, cnt2 = 0;
long n = s.length();
for (int i = 0; i < n; i++)
{
if(s.charAt(i) == '2') {
if(cnt1 + 2 * cnt2 + 2 <= k) cnt2++;
} else {
//chars[i]=='1'
if(cnt1 + 2 * cnt2 + 1 <= k) cnt1++;
else if(cnt2 > 0) {
cnt1++;
cnt2--;
}
}
ans += cnt1 + cnt2; //计入答案
}
System.out.println(ans);
}
}
package content;/**
* Copyright (C), 2019-2021
* author candy_chen
* date 2021/3/14 20:39
*
* @Classname test_01
* Description: 测试
*/
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
*
*/
public class test_01 {
public static void main(String[] args) {
String res = "";
List<String> word = new ArrayList<>();
String s = null;
Scanner sc = new Scanner(System.in);
while (sc.nextLine() != null){
if (s.length() == 0){
break;如果输入为空就停止
}
StringBuilder w = new StringBuilder();
for (int i=0;i<s.length();i++){
//循环s
if (bigletter(s.charAt(i))||smallletter(s.charAt(i)))
w.append(s.charAt(i));//如果是字母则直接压到s里
if (!bigletter(s.charAt(i))&&!smallletter(s.charAt(i))){
//否则把单词压到word了,并把符号也压进去
word.add(w);
w = new StringBuilder();
w.append(s.charAt(i));
word.add(w);
w = new StringBuilder();
}
}
word.add(w);//把最后一个单词压进去
work(word);
word.clear();//清空word
}
}
public static boolean bigletter(char c){
//判断是不是大写字母
if(c>='A'&&c<='Z') return true;
return false;
}
public static boolean smallletter(char c){
//判断是不是小写字母
if (c>='a'&&c<='z') return true;
return false;
}
public static boolean bigletterword(String s){
//判断是不是word
if (!bigletter(s.charAt(0))) return false;
if (s.length()<=1) return false;
for (int i=1;i<s.length();i++){
if (!smallletter(s.charAt(i))) return false;
}
return true;
}
public static void work(List<StringBuilder> word){
//处理每一个单词或符号
int n=word.size();
for (int i=0;i<n;i++){
int inow=i;
if (!bigletterword(String.valueOf(word.toString().length())))//如果不需要缩写则直接输出
System.out.print(word[i]);
if (bigletterword(word[i])) {
if (i!=n-1){
//最后一位要么跟着前面的缩写,要么就是只有一个符合的所以不用缩写
if (word[i+1]==" "&&bigletterword(word[i+2])){
int j;
for (j=i;j<n;j++){
if ((j-i)%2==0){
if (!bigletterword(word[j])){
j--;
break;
}
else
System.out.print(word[j][0]);
}
else if (word[j]!=" ") break;
}//j停在了最后符合要求的word的最后一位的后面一位
System.out.print(" (");
for (int k=i;k<j-1;k+=2)
System.out.print(word[k] + ' ');
System.out.print(word[j-1]+')');
inow=j-1;//i直接跳过缩写的部分
}
else
System.out.print(word[i]);
}
else
System.out.print(word[i]);
}
i=inow;
}
}
}
解题思路:
for循环模拟即可
使用for循环从第1天枚举到第y天。
使用一个临时变量tmp去记录到第i天时,传输机剩下的数据量。
如果到第x天时,tmp += N/2,注意此处的N是一开始的N,即最初的数据量。
表示恢复了一半的数据。
在枚举过程中,每次都要判断当前剩余的数据量是否小于N/32。如果小于,则传
输失败,枚举结束后,如果没有任何传输失败,则还要输出传输了多少数据量。
public class test_09 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
double n = sc.nextInt();
int x = sc.nextInt();
int y = sc.nextInt();
double nn = 0, st = 1;
for (int i = 1; i <= 5; ++i)
st *= 0.5;
for (int i = 1; i <= y; ++i) {
n *= 2.0 / 3;
if (i == x)
n += nn / 2;
if (n < st * nn) {
System.out.println("N0!" );
System.out.println(i +" " + n);
}
}
System.out.println("YE5!");
System.out.println(n);
}
}
题意简述:
有一个 ∗ 的大矩形,同时有若干个a ∗ 的小矩形。小矩形不能旋转。
问最少需要在大矩形中放入多少个小矩形,便再也无法放入更多的矩形。
解题思路:
一道很显然的数学计算题目。设水平方向长为A,竖直方向为B,(小矩形为水平
长a,竖直方向长b)
要使用最少的小矩形让这个大矩形中不能再放入更多的小矩形,显然:要把小矩
形在大矩形中排布的尽可能分散。
即水平方向上的两个小长方形的距离必须小于 a,竖直方向上的两个小矩形的距
离必须小于 b,否则就放得下其他的小长方形了。
public class test_10 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int A = sc.nextInt();
int B = sc.nextInt();
int a = sc.nextInt();
int b = sc.nextInt();
int K = (int) Math.ceil((A - a + 0.000001) / (2 * a - 0.000001));
int C = (int) Math.ceil((B - b + 0.000001) / (2 * b - 0.000001));
System.out.println(K * C);
}
}
解题思路:
第一眼可能觉得此题有点费解,但是数据范围只有5000。
同时要求找最小的和,所以我们不妨使用for循环逐个枚举,令i从最小值1枚举至无穷大。
对于每一个数字i,计算其数位和,设为f(i)。
则可以知道每个数字的数位和都不会太大,可以先开一个数组cnt[x]记录:到目前为止数位
和为x的数字个数。
同时设sum[x]记录:到目前为止数位和为x的数字之和。因为是从小到大顺序枚举的,所以
一定是最小和
本题通过简单枚举和数组即可解决:
在枚举的过程中更新cnt[x]数组和sum[x]数组
在某个cnt[x]的值刚好到n时,用当前的sum[x]去更新答案。
循环结束输出答案即可。
package content;/**
* Copyright (C), 2019-2021
* author candy_chen
* date 2021/3/14 21:11
*
* @Classname test_10
* Description: 测试
*/
import java.util.Scanner;
/**
*
*/
public class test_11 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] cnt = new int[500];
int[] sum = new int[500];
int ans = Integer.MAX_VALUE;
for (int i = 1; i <= 600000; i++) {
int x = f(i);
if(cnt[x] < n) {
cnt[x]++;
sum[x] += i;
}
if (cnt[x] == n) {
ans = Math.min(ans, sum[x]);
}
}
System.out.println(ans);
}
public static int f(int x) {
int tot = 0;
while (x != 0) {
tot += x % 10;
x /= 10;
}
return tot;
}
}
c++代码
#include
#define ll long long
#define re register
using namespace std;
const int N=100005;
int n,m,len,opt;
int pos[N],L[N],R[N];
struct line
{
ll k,b;//斜率和截距
bool operator<(line l)const
{
return k==l.k?b>l.b:k<l.k; //按斜率排序,斜率相同按截距
}
}a[N],b[N];
bool vis[N],use[N],need[N];
int head[N],tail[N]; //第i块在栈中的首尾
int sta[N]; //栈,存直线的下标
double point[N]; //记录交点
inline ll max(ll x,ll y){
return x>y?x:y;}
inline int read()
{
int x=0,f=1;char st=getchar();
while(st<'0'||st>'9'){
if(st=='-') f=-1;st=getchar();}
while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
return x*f;
}
inline void write(ll x)
{
if(x<0) x=~x+1,putchar('-');
if(x>9) write(x/10);
putchar(x%10+'0');
}
inline double getx(line x,line y)
{
return (double)(x.b-y.b)/(y.k-x.k); //算两条直线交点的横坐标
}
inline void rebuild(int x)
{
int st=L[x]-1,top=L[x];
for(re int i=L[x];i<=R[x];++i)
if(vis[i]) b[++st]=a[i]; //把需要重构的直线加入
sort(b+L[x],b+st+1);
sta[top]=L[x];
for(re int i=L[x]+1;i<=st;++i)
if(b[i].k!=b[i-1].k) //如果两条直线斜率相同,只可能取到截距大的一条
{
while(top>L[x]&&getx(b[i],b[sta[top]])<getx(b[sta[top]],b[sta[top-1]])) top--;
point[top]=getx(b[i],b[sta[top]]); //算交点
sta[++top]=i;
}
head[x]=L[x];
tail[x]=top;
}
inline void doit(int t,int l,int r)
{
if(l>r) swap(l,r);
ll tmp=-1e18;
int p=pos[l],q=pos[r];
if(p==q)
{
for(re int i=l;i<=r;++i)
if(vis[i]) tmp=max(tmp,t*a[i].k+a[i].b); //暴力
}
else
{
for(re int i=l;i<=R[p];++i)
if(vis[i]) tmp=max(tmp,t*a[i].k+a[i].b);
for(re int i=L[q];i<=r;++i)
if(vis[i]) tmp=max(tmp,t*a[i].k+a[i].b);
for(re int i=p+1;i<q;++i)
{
if(need[i]) //如果需要重构
{
rebuild(i);
need[i]=0;
}
if(use[i])
{
while(head[i]<tail[i]&&point[head[i]]<t) head[i]++; //找第一个大于等于t的交点
tmp=max(tmp,t*b[sta[head[i]]].k+b[sta[head[i]]].b);
}
}
}
if(tmp==-1e18) puts("baigei");
else write(tmp),puts("");
}
int main()
{
n=read();m=read();
len=sqrt(n/log2(n))+1;
// len=144;
for(re int i=1;i<=n;++i)
pos[i]=(i-1)/len+1;
for(re int i=1;i<=pos[n];++i)
L[i]=R[i-1]+1,R[i]=R[i-1]+len;
R[pos[n]]=n;
while(m--)
{
opt=read();
if(opt==1)
{
int T=read(),x=read(),z=read(),s=read();
vis[x]=1;use[pos[x]]=1;//这个点有,这个块有
need[pos[x]]=1;//需要重构
a[x].k=z;a[x].b=s-(ll)T*z;
}
else
{
int T=read(),l=read(),r=read();
doit(T,l,r);
}
}
return 0;
}