题意: 给出加号和减号个数n,m,以及n+m+1个数,问后缀表达式的最大值是多少。
题解: 加号一般不会有变化,主要是减号可以使负数变正,
1、减号个数为0,那么结果就是所有数的和
2、减号个数不为0:
①、负数个数为0,减去最小的正数例如n=1,m=2, 1 2 3 4 就可以组成4+3-(1-2)
②、负数个数不为0:
a、负数个数==m+n+1,那么肯定有一个负数无法变正,就让最大的负数不为正其他负数为正即可
b、负数个数!=m+n+1,那么肯定能将所有的负数变成正数
package bag;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in) ;
int n=sc.nextInt() ;
int m=sc.nextInt() ;
long[] a = new long[n+m+1] ;
int cnt=0 ;
long sum=0 ;
for(int i=0 ; i<n+m+1 ; ++i) {
a[i]=sc.nextInt() ;
if(a[i]<0) ++cnt ;
sum += a[i] ;
}
Arrays.sort(a,0,m+n+1);
if(m==0) System.out.println(sum);
else {
if(cnt>0) {
if(cnt!=n+m+1) {
for(int i=0 ; i<cnt ; ++i) sum-=2*a[i] ;
}
else
for(int i=0 ; i<cnt-1 ; ++i) sum-=2*a[i] ;
}
else sum -= 2*a[0] ;
System.out.println(sum);
}
}
}
题意: 现有n包糖果,每包糖果内有k颗糖果(给出糖果编号),小明要收集m种糖果,最少要买几包糖果,如果不能全部收集则输出-1
输入n,m,k ,然后是n包糖果内的糖果编号
样例输入:
6 5 3
1 1 2
1 2 3
1 1 3
2 3 5
5 4 2
5 1 2
样例输出:2
题解: 因为m的范围很小1~20 ,所以考虑状态压缩,将糖果编号转化为二进制,在有糖果的数位上标上1,然后将二进制转化为十进制,例如样例:
1 1 2 -> 0 0 0 1 1 (3)
1 2 3 -> 0 0 1 1 1 (7)
1 1 3 -> 0 0 1 0 1 (5)
2 3 5 -> 1 0 1 1 0 (22)
5 4 2 -> 1 1 0 1 0 (26)
5 1 2 -> 1 0 0 1 1 (19)
数组c[]为转化后的十进制数,dp[i]表示i的糖果组合最少需要买几包糖果,初始dp[c[i]]=1(就是上面转化后的十进制数),然后从1~(1<
dp[j|c[i]] == 0 说明 j|c[i] 这种组合情况是没有出现过的可以更新,
dp[j|c[i]] > dp[j]+1 ,说明j|c[i] 这种组合需要买的糖果包数多了,可以以更少的糖果包数组合,所以也要更新
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in) ;
int n = sc.nextInt() ;
int m = sc.nextInt() ;
int k = sc.nextInt() ;
int[] c = new int[1<<21] ;
int[] dp = new int[1<<21] ;
for(int i=0 ; i<n ; ++i) {
for(int j=0 ; j<k ; ++j) {
int a = sc.nextInt() ;
c[i] |= 1<<(a-1) ;
}
// System.out.println("i="+i+" "+c[i]);
dp[c[i]]=1 ;
}
for(int i=0 ; i<n ; ++i) {
for(int j=0 ; j<=(1<<m) ; ++j) {
if(dp[j]==0) continue ;
if(dp[j|c[i]]==0 || dp[j|c[i]]>dp[j]+1)
dp[j|c[i]]=dp[j]+1 ;
}
}
if (dp[(1<<m)-1]!=0) System.out.println(dp[(1<<m)-1]);
else System.out.println("-1");
}
}
题意: 给定n,m,T,n个店铺,m个订单,每个订单包含订单到达的时间ts和店铺的id,每家外卖店都有 一个优先级,初始时 (0 时刻) 优先级都为 0。
每经过 1 个时间单位,如果外卖店没有订单,则优先级会减少 1,最低减 到 0;而如果外卖店有订单,则优先级不减反加,每有一单优先级加 2。
题解: 先按id,ts升序排序,然后模拟过程即可,但是有几个点药注意。
(这里用times记录店铺的优先级),在增加优先级之前要先进行店铺是否退出缓存的判断,即 if(times<=3), 因为在此前面进行了该时间点到上一个时间点的优先级减少,比如有可能优先级已经减少到了2(退出缓存),但是如果增加优先级+2 再进行判断就是 4>3 ,但实际上在收到该订单之前已经退出了缓存。第二个要注意的点就是在结束的时候用 优先级-T-最后订单的时间,然后判断是否退出缓存。
package race;
import java.util.*;
public class Main {
static class node{
int t,id ;
public node(int a,int b) {
t=a;id=b;}
}
static Comparator<node> cmp = new Comparator<node>(){
public int compare(node a,node b) {
if (a.id==b.id) return a.t-b.t ;
return a.id-b.id ;
}
} ;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in) ;
int n = sc.nextInt() ;
int m = sc.nextInt() ;
int T = sc.nextInt() ;
node[] s = new node[m+1] ;
for(int i=0 ; i<m ; ++i) {
int t=sc.nextInt() ;
int id=sc.nextInt() ;
s[i] = new node(t,id) ;
}
Arrays.sort(s,0,m,cmp); //按id优先然后时间升序排序
int cnt=0 ; //记录在T时在缓存的店铺数目
int times=0,j=0 ; //店铺优先级
for(int i=1 ; i<=n&&j<m ; ++i) {
if(s[j].id!=i) continue ;
times=0 ;
int last=0 ;
boolean flag=true ,in=false; //是否为当前店铺的第一个订单
while(j<m&&s[j].id==i) {
if (flag) {
times=2;
flag=false ;
}
else {
if (s[j].t-s[j-1].t>1) times-=s[j].t-s[j-1].t-1 ;
if(times<0) times=0 ;
//判断不在缓存中要在+=2之前判断,因为有可能在这个时间点还没接收到订单之前已经退出了缓存
if(times<=3) in=false;
times+=2 ;
if(times>5) in=true ;
}
++ j ;
}
times -= T-s[j-1].t ; //最后时间点和给定时间点的差值
if(times<=3) in=false ;
if(in) ++cnt;
}
System.out.println(cnt);
}
}
参考博客
讲解视频
package race;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in) ;
int t = sc.nextInt() ;
while(t>0) {
int n = sc.nextInt() ;
long[] s = new long[n+1] ;
long[] st = new long[n+1] ;
boolean[] vis = new boolean[n+1] ;
Arrays.fill(vis,false) ;
s[0]=0 ;
for(int i=1 ; i<=n ; ++i) {
s[i] = sc.nextLong() ;
s[i] += s[i-1] ;
}
long s0=s[0],sn=s[n] ;
if(s0>sn) {
long x=s0;s0=sn;sn=x;}
Arrays.sort(s,0,n+1);
for(int i=0 ; i<=n ; ++i) {
if(s0==s[i]) {
s0=i ;
break ;
}
}
for(int i=n ; i>=0 ; --i) {
if(sn==s[i]) {
sn=i ;
break ;
}
}
int l=0,r=n ;
for(int i=(int)s0 ; i>=0 ; i-=2) {
st[l++] = s[i] ;
vis[i]=true ;
}
for(int i=(int)sn ; i<=n ; i+=2) {
st[r--] = s[i] ;
vis[i]=true ;
}
for(int i=0 ; i<=n ; ++i) {
if(vis[i]==false)
st[l++]=s[i] ;
}
long ans=0 ;
for(int i=1 ; i<=n ; ++i) {
ans = Math.max(ans,Math.abs(st[i]-st[i-1])) ;
}
System.out.println(ans);
--t ;
}
}
}
题意: 给出一串字符串和k,若Alice和Bob之间的字符不超过k,则说明同时出现,询问Alice和Bob同时出现的次数。
题解:
1、输入带有空格字符串使用nextLine() ,前面的k输入后要nextLine()一行才能接收下面的字符串。
2、拆分出字符串的单词,使用字符串的split方法
split(), 括号内写用来分割字符串的字符,这里应该用空格和.分割,使用两种字符分割的时候用 | 来连接,还有要注意的点是 使用 . 要用转义字符,因此是 \.
更多split用法
3、遍历word数组,记录Alice和Bob出现的位置
*4、最最容易超时的点,通过记录的位置找两单词间隔k的个数,一开始我是两重循环寻找然后就超时了,记录的时候两个数组都是从小到大的,所以可以通过这个特性来优化,例如:先寻找Alice后面间隔k字符的Bob个数,先用l记录大于当前Alice出现的位置,r记录在Alice后面间隔k的Bob的位置,然后r-l就是该区间的Bob的数目,然后寻找Bob后面的Alice也是同理。
package race;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in) ;
int k = Integer.parseInt(sc.nextLine()) ;
String s = sc.nextLine() ;
String[] word = s.split(" |\\.") ;
int cur=0;
long cnt=0 ;
int[] a = new int[1000000] ;
int[] b = new int[1000000] ;
int apos=0 , bpos=0 ;
for(int i=0 ; i<word.length ; ++i) {
//记录Alice和Bob出现的位置
if(word[i].equals("Alice")) a[apos++] = cur ;
if(word[i].equals("Bob")) b[bpos++] = cur ;
cur += word[i].length()+1 ;
}
int l=0,r=0 ;
for(int i=0 ; i<apos ; ++i) {
while(l<bpos && b[l]<a[i]) ++l ;
while(r<bpos && b[r]<=a[i]+k+5) ++r ;
cnt += r-l ;
}
l=0 ;r=0 ;
for(int i=0 ; i<bpos ; ++i) {
while(l<apos && a[l]<b[i]) ++l ;
while(r<apos && a[r]<=b[i]+k+3) ++r ;
cnt += r-l ;
}
System.out.println(cnt);
}
}
package race;
import java.util.*;
public class Main {
static Long[] a ;
static int n,k;
public static boolean check(Long x) {
// System.out.println("check : "+x);
long pos=0 ;
for(int i=1 ; i<=k ; ++i) {
long t=0 ;
if(a[i]>pos) t=x-(a[i]-pos-1)*2 ;
else t=x ;
if (t<0) return false ;
pos = Math.min(t/2+a[i],a[i+1]) ;
// System.out.println("pos="+pos+" "+a[i+1]+" "+t);
}
return pos>=n ;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in) ;
n = sc.nextInt() ;
k = sc.nextInt() ;
a = new Long[k+2] ;
a[0]=(long)0 ; a[k+1]=(long)n ;
for(int i=1 ; i<=k ; ++i) a[i]=sc.nextLong() ;
Arrays.sort(a,1,k+1);
long ans=2*n , l=0,r=2*n ;
while(l<r) {
long mid = (l+r)>>1 ;
if(check(mid)) {
ans = Math.min(ans,mid) ;
r=mid ;
}
else l=mid+1 ;
}
System.out.println(ans);
}
}
参考博文
用dfs会超时,emmmm还在研究中,先存个档吧
这个是超时的,AC代码看转载的博文。
package race;
import java.util.*;
public class Main {
static long n,m;
static int alen,blen,k;
static int[] a,b;
static long dp[][][][][] ;
static long mod=1000000007 ;
public static long dfs(int len,int f1,int f2,int f3,int f4) {
//当前长度,i是否上限,j是否上限,i>j , j
if(len==0) return f4 ;
if (dp[len][f1][f2][f3][f4]!=0) return dp[len][f1][f2][f3][f4] ;
long ans=0 ;
int imax=f1==1?a[len]:k-1 , jmax=f2==1?b[len]:k-1;
for(int i=0 ; i<=imax ; ++i) {
for(int j=0 ; j<=jmax; ++j) {
if (f3==0 && i<j) continue ;
ans = (ans+dfs(len-1,(f1==1&&(i==imax))?1:0,
(f2==1&&(j==jmax))?1:0,(f3==1||(i>j))?1:0,(f4==1||i<j)?1:0))%mod ;
}
}
return dp[len][f1][f2][f3][f4]=ans ;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in) ;
int T = sc.nextInt() ;
k=sc.nextInt() ;
while(T>0) {
n=sc.nextLong() ;
m=sc.nextLong() ;
a = new int[70] ;
b = new int[70] ;
long t=n ;
alen=0 ;blen=0 ;
while(t>0) {
a[++alen]=(int)(t%k); t/=k;}
t=m ;
while(t>0) {
b[++blen]=(int)(t%k); t/=k;}
dp = new long[Math.max(alen, blen)+1][2][2][2][2] ;
System.out.println(dfs(Math.max(alen,blen),1,1,0,0));
--T ;
}
}
}
1、先对n进行因式分解,求出q和p,计算mod=(p-1)(q-1)
2、e*d%mod = 1 , e = (1/d)%mod , 即e为d的逆元,利用扩展欧几里得求出d的逆元,要注意这里的mod不是质数所以不能用费小马定理来求得逆元
扩展欧几里得、求逆元的三种方法
3、更具题意的公式求得C,但是要注意这里会爆long所以可以使用java的大数类BigInteger
package race;
import java.math.BigInteger;
import java.util.*;
public class Main {
static long x,y ;
public static void exgcd(long a,long b) {
if(b==0) {
x=1;y=0 ;
return ;
}
exgcd(b,a%b) ;
long t=x ; x=y ;
y = t-a/b*y ;
}
public static long fastpow(long n,long a,long mod) {
long res=1 ;
n %= mod ;
while(a>0) {
if((a&1)==1) res = res*n%mod ;
n = n*n%mod ;
a>>=1 ;
}
return res ;
}
static long p,q ;
public static void init(long n) {
for(int i=2 ; i*i<=n ; ++i) {
if(n%i==0&&isprime(i)&&isprime(n/i)) {
p=i ; q=n/i ;
return ;
}
}
}
public static boolean isprime(long n) {
for(long i=2 ; i*i<=n ; ++i)
if(n%i==0) return false ;
return true ;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in) ;
long n= 1001733993063167141L ;
long d=212353 ;
long c=20190324 ;
// long p=891234941,q=1123984201 ; //q p答案
init(n) ; //因式分解n,求p和q
long mod=(p-1)*(q-1) ;
exgcd(d,mod) ; //扩展欧几里得求逆元
long e=(x+mod)%mod ;
/* 由于mod=(p-1)(q-1) 不是质数,所以不能使用费小马定理来求d的逆元
long cur = fastpow(n,mod-2,mod) ;
System.out.println(isprime(mod));
System.out.println(cur);
*/
BigInteger ans = BigInteger.valueOf(c) ;
BigInteger cc = BigInteger.valueOf(e) ;
BigInteger nn = BigInteger.valueOf(n) ;
ans = ans.modPow(cc,nn) ;
System.out.println(ans);
System.out.println(e);
}
}
答案:
ans = 579706994112328949
e = 823816093931522017
C++ 版,c++没有大数类所以在快速幂的时候要用到快速乘法
#include
#include
using namespace std ;
typedef long long ll ;
ll x,y,p,q ;
void exgcd(ll a,ll b){
ll t ;
if(b==0){
x=1,y=0 ;
return ;
}
exgcd(b,a%b) ;
t=x , x=y ;
y=t-a/b*y ;
}
ll fastmul(ll n,ll m,ll mod){
ll ans=0 ;
while(m){
if(m&1) ans=(ans+n)%mod ;
n=(n<<1)%mod ;
m>>=1 ;
}
return ans ;
}
ll fastpow(ll n,ll a,ll mod){
ll res=1 ;
while(a){
if(a&1) res=fastmul(res,n,mod) ;
n=fastmul(n,n,mod) ;
a >>=1 ;
}
return res ;
}
bool isprime(ll n){
for(ll i=2 ; i*i<=n ; ++i)
if(n%i==0) return false ;
return true ;
}
void init(ll n){
for(ll i=2 ; i*i<=n ; ++i){
if(n%i==0&&isprime(i)&&isprime(n/i)){
p=i , q=n/i ;
return ;
}
}
}
int main(){
ll n= 1001733993063167141,d=212353,c=20190324 ;
init(n) ;
ll mod=(p-1)*(q-1) ;
exgcd(d,mod) ;
ll e = (x+mod)%mod ;
printf ("p=%lld q=%lld\n",p,q) ;
printf ("e=%lld\n",e) ;
ll ans = fastpow(c,e,n) ;
printf ("ans=%lld\n",ans%n) ;
return 0 ;
}
package race;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in) ;
char[] s = {
'L','A','N','Q','I','A','O'} ;
long ans=0 ;
long pos=0 ,t=1;
for(int i=s.length-1 ; i>=0 ; --i) {
int idx = s[i]-'A'+1 ;
ans += t*idx ;
t *= 26 ;
}
System.out.println(ans);
}
}