CodeForces 915C [DFS] CodeForces 158E [DP]

本文探讨了两道CodeForces题目,915C如何通过重排数字构造不超过b的最大数,以及158E如何在允许忽略一定数量电话的情况下最大化休息时间。解决方案涉及深度优先搜索(DFS)和动态规划(DP)策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CodeForces 915C Permute Digits

http://codeforces.com/problemset/problem/915/C

Problem Description

You are given two positive integer numbers a and b. Permute (change order) of the digits of a to construct maximal number not exceeding b. No number in input and/or output can start with the digit 0.

It is allowed to leave a as it is.

Input

The first line contains integer a (1 ≤ a ≤ 1018). The second line contains integer b (1 ≤ b ≤ 1018). Numbers don’t have leading zeroes. It is guaranteed that answer exists.

Output

Print the maximum possible number that is a permutation of digits of a and is not greater than b. The answer can’t have any leading zeroes. It is guaranteed that the answer exists.

The number in the output should have exactly the same length as number a. It should be a permutation of digits of a.

Examples

input 1

123
222

output 1

213

input 2

3921
10000

output 2

9321

input 3

4940
5000

output 3

4940

中文大意

已知a,b两个数,求将a的各个数码重排后,能得出的不大于b的最大数。a,b不超过18位,且a可以不变。

分析

首先想到,只需保存a中0~9的数码个数。如果a的位数比b少,直接输出最大可能的a,因为无论其数字位置变化,都有 a<b a < b

对于a,b位数一样的情况,我们一位一位的分析。为保证 a 与 b 最接近,高位一定是尽量取得与 b 一样。因此首先考虑对于倒数第 i 位数(从最高位数起的第 i 位)而a,b,前面的数码都一模一样。此时如果在 a 使用了前面位数的数字后仍然剩下数码 x,且 x 与 b 的第 i 位相等,在这种情况下,为了保证 a<=b a <= b 那么我们先暂时取该位就为 x。 因为前面的数码都一样,我们只用考虑后面能不能得到一个数串使得i位后面的a不比i位后面的b大。怎么判断呢?如果出现一个局面,在取到第j位时,使得a剩下可取的数都比b的第j位数码大,那么这样就一定不能满足题目要求。假如出现这种局面,我们需要返回到前面最近的,且这一位中有剩余数码x,使得x比b的该位小,而我们不得不放弃之前取的与b相同的数码,改为这个数码x,这样后面的位数无论如何取,都有 a<b a < b 了,于是后面就改成可以取的最大数即可。

描述很复杂,实际操作就是用一个bool dfs, 保存局面,然后先看a在该位能否取得与b一样,如果a没有等于b该位的数,且有至少一个小于b该位的数,那么一定行,就直接打印前面与b一样,后面为剩下的最大数串,并返回true。如果a没有小于等于b该位的数可取,那一定不行,返回false。若a可以取得与b该位一样的数,那么先看能不能取一样,如果返回false,则改取得最大的且小于b该位的数,如果有,输出结果,没有就再返回false。

总结

这个题反正方法比较简单,只是有些特判不能忘记,还有就是题上说了,保证有答案。

这个题tag的是dp,大概是有问题吧,不用保存状态,也不会重复取,哪来的动态规划?

在测试程序时,可以用以下几组数据来测:

input

4322
4321

output

4232

input

10000
10000

output

10000

参考代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int d[15],b[25],lena,lenb,res[25];
void solve1()
{
    for(int i=9;i>=0;i--)
    while(d[i]--)
        printf("%d",i);
}
bool f(int digit[15],int cur)
{
    if(cur>lenb)
    {
    for(int i=1;i<cur;i++)
        printf("%d",res[i]);
    return true;
    }
    if(digit[b[cur]])
    {
    digit[b[cur]]--;
        res[cur]=b[cur];
        if(f(digit,cur+1))
        return true;
        digit[b[cur]]++;
    }
    bool flag=false;
    for(int i=b[cur]-1;i>=0;i--)
    if(digit[i])
    {
        flag=true;
        res[cur]=i;
        digit[i]--;
        break;
    }

    if(flag)
    {
    for(int i=1;i<=cur;i++)
        printf("%d",res[i]);
    for(int i=9;i>=0;i--)
        while(digit[i]--)
        printf("%d",i);
    return true;
    }
    return false;   
}
int main()
{
    char a;
    while((a=getchar())>='0')
    d[a-48]++,lena++;
    while((a=getchar())>='0')
    b[++lenb]=a-48;
    if(lenb>lena)
    solve1();
    else
    f(d,1);
    printf("\n");
}

下面一道真正的dp题。

CodeForces 158E Phone Talks

http://codeforces.com/problemset/problem/158/E

Problem Description

Cool J has recently become a businessman Mr. Jackson, and he has to make a lot of phone calls now. Today he has n calls planned. For each call we know the moment ti (in seconds since the start of the day) when it is scheduled to start and its duration di (in seconds). All ti are different. Mr. Jackson is a very important person, so he never dials anybody himself, all calls will be incoming.

Mr. Jackson isn’t Caesar and he can’t do several things at once. If somebody calls him while he hasn’t finished the previous conversation, Mr. Jackson puts the new call on hold in the queue. In this case immediately after the end of the current call Mr. Jackson takes the earliest incoming call from the queue and starts the conversation. If Mr. Jackson started the call at the second t, and the call continues for d seconds, then Mr. Jackson is busy at seconds t, t + 1, …, t + d - 1, and he can start a new call at second t + d. Note that if Mr. Jackson is not busy talking when somebody calls, he can’t put this call on hold.

Mr. Jackson isn’t Napoleon either, he likes to sleep. So sometimes he allows himself the luxury of ignoring a call, as if it never was scheduled. He can ignore at most k calls. Note that a call which comes while he is busy talking can be ignored as well.

What is the maximum number of seconds Mr. Jackson can sleep today, assuming that he can choose an arbitrary continuous time segment from the current day (that is, with seconds from the 1-st to the 86400-th, inclusive) when he is not busy talking?

Note that some calls can be continued or postponed to the next day or even later. However, the interval for sleep should be completely within the current day.

Input

The first input line contains a pair of integers n, k (0 ≤ k ≤ n ≤ 4000) separated by a space. Following n lines contain the description of calls for today. The description of each call is located on the single line and consists of two space-separated integers ti and di, (1 ≤ ti, di ≤ 86400). All ti are distinct, the calls are given in the order of strict increasing ti.

Scheduled times of calls [ti, ti + di - 1] can arbitrarily intersect.

Output

Print a number from 0 to 86400, inclusive — the maximally possible number of seconds for Mr. Jackson to sleep today.

Examples

input

3 2
30000 15000
40000 15000
50000 15000

output

49999

input

5 1
1 20000
10000 10000
20000 20000
25000 10000
80000 60000

output

39999

Note

In the first sample the most convenient way is to ignore the first two calls.

In the second sample it is best to ignore the third call. In this case Mr. Jackson will have been speaking:

first call: from 1-st to 20000-th second,
second call: from 20001-st to 30000-th second,
fourth call: from 30001-st to 40000-th second (the third call is ignored),
fifth call: from 80000-th to 139999-th second.
Thus, the longest period of free time is from the 40001-th to the 79999-th second.

中文大意

J有n个电话要接,每个电话打进来的时刻为第t[i]分钟,时长为d[i]分钟。
每一个电话打进来时,J有两种选择:

1.将电话挂掉(最多挂k次,不占时间)
2.接听(如果当时正在打其他电话,则这个电话加入等待队列,等上一个电话打完后立马接听)

时间从第1秒开始算,一直到86400秒结束,问你能够他能获得的最长连续间隔为多长时间。

分析

首先,很显然一定要挂k次才优。

简单证明就是,如果挂k-1次比挂k次还优,那若在挂k-1的基础上再挂一次任意一个电话,一定有 Ansk1>=Ansk A n s k − 1 >= A n s k ,那肯定挂得越多越好,至于怎么挂最好,后面再讨论。

要使得空当最大,如果空当过后就是接听第 i 个电话,空当时间为 StartiEndi1 S t a r t i − E n d i − 1 这个 Starti S t a r t i 有两种情况,要么等于 ai a i ,要么大于 ai a i ,而如果大于 ai a i 就是说没有i-1与i没有空当,那就与假设矛盾了,所以这里的 Starti S t a r t i 一定等于 ai a i ,而 ai a i ,如果打了这个电话,就一定不变,如果挂掉,则与假设空当过后接的第一个电话矛盾,那么这个 Starti S t a r t i 是没法变了,变的只能是 Endi1 E n d i − 1 。要使空当最大,那 Endi1 E n d i − 1 不可避免地就要尽量小。

因此设dp[i][j] 为完成第i个电话,挂掉了j个电话的最短时间,最后求一个 Max(aidp[i1][k]1) M a x ( a i − d p [ i − 1 ] [ k ] − 1 ) 就完了(减1是因为 ai a i 时刻在打电话,不能算入空当)。
那dp方程就变得简单
对于dp[i][j],状态有两种:
1.挂掉第i个电话,用去一次机会,那 dp[i][j]=dp[i1][j1] d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] ,意思就是说相当于完成上一个电话的最短时间
2.不挂掉第i个电话,打完这个电话,那 dp[i][j]=max(dp[i1][j]+1,a[i])+d[i] d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] + 1 , a [ i ] ) + d [ i ] ,取max是因为有可能i 这个电话是在队列里,如果是的话就直接接听(+1是因为接听是从新的一秒开始算),不是就等到它打进来。

所以最终 dp[i][j]=min(dp[i1][j1],max(dp[i1][j]+1,a[i])+d[i]) d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j − 1 ] , m a x ( d p [ i − 1 ] [ j ] + 1 , a [ i ] ) + d [ i ] )

最后再用前面说的 Max(aidp[i1][k]1) M a x ( a i − d p [ i − 1 ] [ k ] − 1 ) 遍历一遍算结果就好

总结

这道题解决方法其实很巧妙,而注意点就是记得最后我们为了方便计算,假设在第86401秒打了一个持续0秒的电话。(一定是从86401,而不是86400开始打,不然就会多占一秒)还有就是特判不能少,比如当k==n就直接输出86400。

参考代码

#include<cstdio>
using namespace std;
int N,K,a[4005],d[4005],f[4005][4005],ans;
int main()
{
    scanf("%d%d",&N,&K);
    for(int i=1;i<=N;i++)
    scanf("%d%d",&a[i],&d[i]);
    a[N+1]=86401;//一定是86401
    if(N==K) ans=86400;//特判
    else
        for(int i=1;i<=N;i++)
        {
            f[i][0]=max(f[i-1][0]+d[i],a[i]+d[i]-1);
            for(int j=1;j<=K&&j<=i;j++)
                f[i][j]=min(max(f[i-1][j]+d[i],a[i]+d[i]-1),f[i-1][j-1]);
            ans=max(ans,a[i+1]-f[i][K]-1);//边算边直接求答案
        }
    ans=max(ans,a[1]-1);
    printf("%d\n",ans);
    return 0;
}

P.S. AFO过后已经一年没碰OI了,真的是一点都没碰,这一年除了浅学了Python 和 Java 什么都没干,而且最后发现还是C++亲切。
P.P.S. 以后将尽量提供英文版解析。

Codeforces比赛中,编写高效、简洁的代码是取得好成绩的关键之一。选手通常会依赖一些常用的代码模板或框架,以节省时间并减少出错的可能性。以下是一些在Codeforces比赛中常见的代码模板及其用途: ### 输入输出模板 在竞赛中,标准输入输出的速度对性能有一定影响,尤其是当数据量较大时。Java中通常使用`BufferedReader`和`PrintWriter`来提高输入输出效率。 ```java import java.io.*; import java.util.*; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out)); String line; while ((line = br.readLine()) != null) { pw.println(line); } pw.flush(); } } ``` ### 快速读取整数数组 对于需要频繁读取多个整数的情况,可以使用以下方法将一行输入拆分为整数数组。 ```java StringTokenizer st = new StringTokenizer(br.readLine()); int n = Integer.parseInt(st.nextToken()); int[] arr = new int[n]; for (int i = 0; i < n; i++) { arr[i] = Integer.parseInt(st.nextToken()); } ``` ### 数学函数模板 一些常见的数学运算可以预先定义为函数,例如最大公约数(GCD)、快速幂等。 ```java public static int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } public static long fastPower(long base, long exponent, long mod) { long result = 1; while (exponent > 0) { if ((exponent & 1) == 1) { result = (result * base) % mod; } base = (base * base) % mod; exponent >>= 1; } return result; } ``` ### 数据结构模板 使用Java集合框架时,可以预先准备好一些常用的数据结构操作,例如优先队列、双端队列等。 ```java PriorityQueue<Integer> minHeap = new PriorityQueue<>(); Deque<Integer> deque = new ArrayDeque<>(); ``` ### 图论算法模板 图论问题中,常见的算法如广度优先搜索(BFS)、深度优先搜索(DFS)、最短路径算法(Dijkstra)等可以预先准备好模板。 ```java // BFS模板 public static void bfs(int start, List<List<Integer>> adj, boolean[] visited) { Queue<Integer> queue = new LinkedList<>(); queue.add(start); visited[start] = true; while (!queue.isEmpty()) { int node = queue.poll(); for (int neighbor : adj.get(node)) { if (!visited[neighbor]) { visited[neighbor] = true; queue.add(neighbor); } } } } ``` ### 动态规划模板 动态规划是竞赛中常见的解题方法。可以根据问题的特性设计状态转移方程。 ```java int n = 100; int[] dp = new int[n + 1]; dp[0] = 0; for (int i = 1; i <= n; i++) { dp[i] = Integer.MAX_VALUE; for (int j = 1; j * j <= i; j++) { dp[i] = Math.min(dp[i], dp[i - j * j] + 1); } } ``` ### 字符串处理模板 字符串处理中,常见的操作如字符串匹配、回文检查等也可以预先准备好。 ```java public static boolean isPalindrome(String s) { int left = 0, right = s.length() - 1; while (left < right) { if (s.charAt(left) != s.charAt(right)) { return false; } left++; right--; } return true; } ``` 以上模板可以作为Codeforces比赛中的常用代码框架,选手可以根据具体题目要求进行调整和优化。此外,建议平时刷题时将这些模板保存起来,以便比赛时快速调用[^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值