1.算式问题(模拟)
public class Main {
public static int f(int i,int j,int k)
{
int []v=new int [10];
for(int l=0;l<10;l++)
v[l]=0;
v[0]=1;//这里因为会遇到有0的情况(如307)
while(i>0)
{
if(v[i%10]==1) return 0;
else v[i%10]=1;
if(v[j%10]==1) return 0;
else v[j%10]=1 ;
if(v[k%10]==1) return 0;
else v[k%10]=1 ;
i/=10;
j/=10;
k/=10;
}
return 1;
}
int []v=new int [1000];
public static void main(String[] args)
{
Scanner cin=new Scanner (System.in);
int sum=0;
for(int i=123;i<=987;i++)
{
for(int j=123;j<=987-i;j++)
{
int k=i+j;
if(f(i,j,k)==1)
sum++;
}
}
System.out.print(sum);
}
}
2. 跳跃的小明(动态规划)
import java.math.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;
import java.util.HashSet;
import java.util. Iterator;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
public class Main {
public static void main(String[] args)
{
Scanner cin=new Scanner (System.in);
int n,p,t;
n=cin.nextInt();
p=cin.nextInt();
t=cin.nextInt();
int []arr=new int [2500];
int [][]dp=new int [2500][2500];//dp[i][j]:一共跳了j次到达了第i格
for(int i=1;i<=n;i++)
{arr[i]=cin.nextInt();}
for(int []a:dp)//memset
{
Arrays.fill(a,-0x3f3f3f);
}
for(int i=1;i<=n+1&&i<=p;i++)//p的最小值是1,因此是i<=n+1;i<=n+1&&i<=p是因为p可能大于n
dp[i][1]=arr[i];
/*第i格*/ for(int i=1;i<=n+1;i++)
{
/*走的步数*/ for(int j=2;j<=t;j++)
{
/*上一个走了k步到达第i格*/ for(int k=1;k<=p&&k<i;k++)
{
dp[i][j]=Math.max(dp[i][j], dp[i-k][j-1]+arr[i]);
}
}
}
System.out.print(dp[n+1][t]);
}
}
3. 路径(动态规划)
import java.math.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;
import java.util.HashSet;
import java.util. Iterator;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
public class Main {
public static int gcd(int a,int b)
{
if(b==0) return a;
else return gcd(b,a%b);
}
public static int lcs(int a,int b)
{
return a/gcd(a,b)*b;
}
public static void main(String[] args)
{
Scanner cin=new Scanner(System.in);
int dp[]=new int [200000];
for(int i=1;i<=2021;i++)
{
for(int j=i+1;j<=i+21;j++)
{
if(j>2021) break;
if(dp[j]==0) dp[j]=dp[i]+lcs(i,j);//这一步很细节
dp[j]=Math.min(dp[j], dp[i]+lcs(i,j));
}
}
System.out.print(dp[2021]);
}
}
4. 分考场(dfs)
import java.util.*;
public class Main {
static int n,m,N=110;
static List<Integer>[] list=new ArrayList[N];
static int ans=100;
static boolean[][] map=new boolean[N][N];
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
for (int i = 0; i < m; i++) {
int a=sc.nextInt();
int b=sc.nextInt();
map[a][b]=map[b][a]=true;
}
for (int i = 1; i<=n ; i++) {
list[i]=new ArrayList<>();
}
dfs(1,0);
System.out.println(ans);
}
//st是学生,cl是教室数量
static void dfs(int st,int cl){
//这步是剪枝操作,很重要,否则可能导致超时
//当我们判断已用教室已经大于等于找到的最小情况
//那我们没有必须继续搜索,因为它一定不是答案
if (cl>=ans) return;
//到这一步说明找到了一种更小的方案
if (st>n){
ans=cl;
return;
}
for (int i = 1;i<=cl ; i++) {
int j=0;
for (;j<list[i].size();++j){
if(map[st][list[i].get(j)]) break;
}
//说明和这个教室全部学生都不相等
if (j==list[i].size()){
//放入学生
list[i].add(st);
dfs(st+1,cl);
//回溯
list[i].remove(list[i].size()-1);
}
}
//走到这步还没找到教室说明需要新教室
list[cl+1].add(st);
dfs(st+1,cl+1);
//回溯
list[cl+1].remove(list[cl+1].size()-1);
}
}
4. 二分答案
1.最大化最小值(靠左查找)
while (l < r)
{
int mid = l + r >> 1; //(l+r)/2
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
2.最小值的最大(靠右查找)
while (l < r)
{
int mid = l + r + 1 >> 1; //(l+r+1)/2
if (check(mid)) l = mid;
else r = mid - 1;
}
3.浮点二分
while(r-l>1e-5) //需要一个精度保证,看题意
{
double mid = (l+r)/2;
if(check(mid)) l=mid; //或r=mid;
else r=mid; //或l=mid;
}
至于check函数中的判断语句怎么写,先判断是哪个模板,之后看对应if(…)右边的是r还是l,如果是r则说明找大的
打包(二分答案)
import java.util.Scanner;
public class Main {
static int N=100010;
static int[] arr=new int[N];
static int n,m;
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
int max=0;
int sum=0;
for(int i=1;i<=n;++i) {
arr[i]=sc.nextInt();
sum+=arr[i];
max=Math.max(max, arr[i]);
}
int l=max;
int r=sum;
while(l<r) {
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
System.out.println(l);
}
static boolean check(int target) {
int count=0;
int tmp=0;
for(int i=1;i<=n;++i) {
count+=arr[i];
if(count>target){
count=0;
tmp++;
i--;
}else if(i==n){
tmp++;
}
}
return tmp<=m;
}
}
5. 合根植物(并查集)
import java.util.Scanner;
public class Main {
static int N=1010;
static int[][] arr=new int[N][N];
//并查集
static int[] p=new int[N*N];
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int m=sc.nextInt();
int count=m*n;
for(int i=1;i<=n*m;++i) p[i]=i;
int k=sc.nextInt();
while(k-->0) {
int a=sc.nextInt();
int b=sc.nextInt();
int root1=find(a);
int root2=find(b);
if(root1!=root2){
p[root1]=root2;
count--;
}
}
System.out.println(count);
}
static int find(int x) {
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
}
5. 汉诺塔
6. 整数分解(dp)
/*https://www.luogu.com.cn/paste/miyd5na7*/
package zkj;
import java.math.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;
import java.util.HashSet;
import java.util. Iterator;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
public class Zz {
public static void main(String[] args)
{
Scanner cin=new Scanner (System.in);
long [][]dp=new long[2500][2500];
for(long []a: dp)
{
Arrays.sort(a);
}
for(int i=1;i<=2021;i++)
{
dp[1][i]=1;
}
for(int i=2;i<=5;i++)
{
for(int j=1;j<=2021;j++)
{
for(int k=1;k<=2021;k++)
{
if(j-k>0)
dp[i][j]+=dp[i-1][j-k];
}
}
}
System.out.print(dp[5][2021]);
}
}
7. 城邦(并查集+Kruskal)
Kruskal:对边贪心最小生成树
class node
{
int a,b,c;
}
class mycompare implements Comparator<node>{
public int compare(node x,node y)
{
return x.c-y.c;
}
}
public class Zz {
static int n=2021;
static node []e=new node [n*n];
static int father[]=new int [2023];
static int find(int x)
{
if(x!=father[x])
{
father[x]=find(father[x]);
}
return father[x];
}
static int get(int i,int j)
{
int res=0;
int maxn=Math.max(i,j);
while(maxn>0)
{
int aa=i%10;
int bb=j%10;
if(aa!=bb)
{
res+=aa+bb;
}
i/=10;
j/=10;
maxn/=10;
}
return res;
}
public static void main(String[] args)
{
Scanner cin=new Scanner (System.in);
for(int i=1;i<=2021;i++)
father[i]=i;
int m=0;
for(int i=1;i<=2021;i++)
{
for(int j=1;j<=2021;j++)
{
e[m]=new node();
e[m].a=i;
e[m].b=j;
e[m].c=get(i,j);
m++;
}
}
Arrays.sort(e,0,m,new mycompare());
long res=0;
for(int i=0;i<m;i++)
{
int x=e[i].a;
int y=e[i].b;
int z=e[i].c;
if(find(x)!=find(y))
{
res+=e[i].c;
father[find(x)]=find(y);
}
}
System.out.print(res);
}
}
8.最短路(Floyd+临接矩阵)
void floyd()
{
for (int k = 1; k <= n; k ++ ) //通过n个点
for (int i = 1; i <= n; i ++ ) // 遍历所有点对。
for (int j = 1; j <= n; j ++ )
arr[i][j] = min(arr[i][j], arr[i][k] + arr[k][j]);
}
9.递增三元组(暴力)
#include<iostream>
#include<algorithm>
using namespace std;
int a[100005],b[100005],c[100005];
typedef long long ll;
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
for(int i=0;i<n;i++)
cin>>b[i];
for(int i=0;i<n;i++)
cin>>c[i];
sort(a,a+n);
sort(b,b+n);
sort(c,c+n);
int p1=0,p2=0;
ll sum=0;
for(int i=0;i<n;i++)
{
while(p1<n&&a[p1]<b[i]) p1++;
while(p2<n&&c[p2]<=b[i]) p2++;
sum+=(ll)p1*(n-p2);
}
cout<<sum<<endl;
return 0;
}
总结
1.二维数组的初始化任何值,其中下面的dp数组是我们要初始化的对象,
int [][]dp=new int [25][25];
for(int []a:dp)//memset
{
Arrays.fill(a,-0x3f3f3f);
}
2.二维vector的使用
ArrayList<Integer>list[]=new ArrayList[100];
for(int i=1;i<=n;i++)
{
list[i]=new ArrayList();
}
3.二分答案套模板
4.dp模板
import java.math.*;
import java.util.*;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner cin=new Scanner(System.in);
int n;
int []a=new int [50];
int []dp=new int [50];
int maxn=-9999;
n=cin.nextInt();
for(int i=1;i<=n;i++)
{ a[i]=cin.nextInt(); dp[i]=a[i];}
/*for(int i=1;i<=n;i++)
{
dp[i]=1;
for(int j=1;j<i;j++)
{
if(a[i]>a[j])
dp[i]=Math.max(dp[i], dp[j]+1);
}
maxn=Math.max(dp[i], maxn);
}*/
for(int i=1;i<=n;i++)
{
dp[i]=Math.max(dp[i], dp[i-1]+a[i]);
}
for(int i=1;i<=n;i++) maxn=Math.max(maxn, dp[i]);
System.out.print(maxn);
}
}
/*#include<bits/stdc++.h>
using namespace std;
//求最小全装满的方案数
int a[250];
int dp[250];
const int maxn=0x3f3f3f;
int main()
{
//cin>>n>>sum;
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
dp[0]=0;
for(int i=1;i<=m+1;i++)
dp[i]=maxn;
for(int i=1;i<=n;i++)
{
for(int j=a[i];j<=m;j++)
{
dp[j]=min(dp[j],dp[j-a[i]]+1);
}
}
cout<<dp[m];
}
*/
#include<bits/stdc++.h>
using namespace std;
//求最大全装满的方案数
int a[250];
int dp[250];
const int maxn=0x3f3f3f;
int main()
{
//cin>>n>>sum;
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
dp[0]=0;
for(int i=1;i<=m+1;i++)
dp[i]=-maxn;
for(int i=1;i<=n;i++)
{
for(int j=m;j>=a[i];j--)
{
dp[j]=max(dp[j],dp[j-a[i]]+1);
}
}
cout<<dp[m];
}
#include<bits/stdc++.h>
#include<iostream>
#include<cstring>
using namespace std;
int n,m;//itn dp[m][n]
int dp[500][500];
int main(){
cin>>n>>m;
dp[0][0]=1;
for(int i=1;i<=m;i++)
{
for(int j=0;j<=n;j++)
{
if(j-i>=0)
{
dp[i][j]=dp[i-1][j]+dp[i][j-i];
}
else {
dp[i][j]=dp[i-1][j];
}
}
}
cout<<dp[m][n];
}