BestCoder Round #75

16 篇文章 0 订阅

此处有目录↑


HDU-5640 King's Cake (模拟)

http://acm.hdu.edu.cn/showproblem.php?pid=5640

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)


Problem Description
It is the king's birthday before the military parade . The ministers prepared a rectangle cake of size  n×m(1n,m10000)  . The king plans to cut the cake himself. But he has a strange habit of cutting cakes. Each time, he will cut the rectangle cake into two pieces, one of which should be a square cake.. Since he loves squares , he will cut the biggest square cake. He will continue to do that until all the pieces are square. Now can you tell him how many pieces he can get when he finishes.
 

Input
The first line contains a number  T(T1000) , the number of the testcases.

For each testcase, the first line and the only line contains two positive numbers  n,m(1n,m10000) .
 

Output
For each testcase, print a single number as the answer.
 

Sample Input
  
  
2 2 3 2 5
 

Sample Output
  
  
3 4 hint: For the first testcase you can divide the into one cake of 2*2 , 2 cakes of 1*1

题目大意:有一块n*m的蛋糕,每次只切1刀,切下一个正方形的蛋糕,问最多有多少块正方形的蛋糕?

大致思路:由于只能切一刀,所以必定是以较短边为变长的正方形,剩下的矩形蛋糕继续重复切蛋糕的过程

只想到裸的模拟,没有想到优化后的方法,幸亏没有卡

#include <cstdio>

using namespace std;

int main() {
    int T,a,b,ans;
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&a,&b);
        ans=0;
        while(a>0&&b>0) {
            ++ans;
            if(a>b) {
                a-=b;
            }
            else if(a<b) {
                b-=a;
            }
            else
                break;
        }
        printf("%d\n",ans);
    }
    return 0;
}

优化后大致这样:

#include <cstdio>

using namespace std;

int main() {
    int T,a,b,ans;
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&a,&b);
        ans=0;
        while(a>0&&b>0) {
            if(a>b) {//用除法和取模快速求得在某一方向连续切割的次数,防止 1 1000 这样的极端数据
                ans+=a/b;
                a%=b;
            }
            else {
                ans+=b/a;
                b%=a;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}



HDU-5641 King's Phone (模拟)

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)


Problem Description
In a military parade, the King sees lots of new things, including an Andriod Phone. He becomes interested in the pattern lock screen.

The pattern interface is a  3×3  square lattice, the three points in the first line are labeled as  1,2,3 , the three points in the second line are labeled as  4,5,6 , and the three points in the last line are labeled as  7,8,9 。The password itself is a sequence, representing the points in chronological sequence, but you should follow the following rules:

- The password contains at least four points.


- Once a point has been passed through. It can't be passed through again.

- The middle point on the path can't be skipped, unless it has been passed through( 3427  is valid, but  3724  is invalid).

His password has a length for a positive integer  k(1k9) , the password sequence is  s1,s2...sk(0si<INT_MAX)  , he wants to know whether the password is valid. Then the King throws the problem to you.
 

Input
The first line contains a number&nbsp; T(0<T100000) , the number of the testcases.

For each test case, there are only one line. the first first number&nbsp; k ,represent the length of the password, then  k  numbers, separated by a space, representing the password sequence  s1,s2...sk .
 

Output
Output exactly  T  lines. For each test case, print `valid` if the password is valid, otherwise print `invalid`
 

Sample Input
  
  
3 4 1 3 6 2 4 6 2 1 3 4 8 1 6 7
 

Sample Output
  
  
invalid valid valid hint: For test case #1:The path 1→3 skipped the middle point $2$, so it's invalid. For test case #2:The path 1 3 doesn't skipped the middle point $2$, because the point 2 has been through, so it's valid. For test case #2:The path 8 1 6 7 doesn't have any the middle point 2, so it's valid.

题目大意:判断一个安卓图形解锁是否合法。合法的密码满足一下规则:
1. 密码至少经过四个点。
2. 不能重复经过同一个点。
3. 路径上的中间点不能跳过,除非已经被经过(34273427 是合法的,但 37243724 不合法)

大致思路:规则1,2很好判断,规则3只需要开一个数据cross[i][j],表示i,j之间必须经过的点是cross[i][j],为了方便,用0表示不用经过点,并且vis[0]=true

坑点:点是从0开始一直可以取到int最大值

写的时候偷懒,至判断了当前的点是否在1~9之间,忘了判断上一个点,导致终测时越界

#include <cstdio>
#include <cstring>

using namespace std;

const int mod=1000000007;

int T,k,num[11],cross[11][11];
bool vis[11],valid;

int main() {
    memset(cross,0,sizeof(cross));
    cross[1][3]=cross[3][1]=2;
    cross[1][7]=cross[7][1]=4;
    cross[1][9]=cross[9][1]=5;
    cross[2][8]=cross[8][2]=5;
    cross[3][7]=cross[7][3]=5;
    cross[3][9]=cross[9][3]=6;
    cross[4][6]=cross[6][4]=5;
    cross[7][9]=cross[9][7]=8;
    scanf("%d",&T);
    while(T--) {
        valid=true;
        memset(vis,false,sizeof(vis));
        scanf("%d",&k);
        if(k<4)
            valid=false;
        for(int i=0;i<k;++i)
            scanf("%d",num+i);
        if(1<=num[0]&&num[0]<=9) {
            vis[0]=vis[num[0]]=true;
            for(int i=1;i<k;++i) {
                if(1<=num[i]&&num[i]<=9) {
                    if(vis[num[i]])
                        valid=false;
                    else
                        vis[num[i]]=true;
                    if(!vis[cross[num[i-1]][num[i]]])
                        valid=false;
                }
                else {
                    valid=false;
                    break;
                }
            }
        }
        else
            valid=false;
        printf("%s\n",valid?"valid":"invalid");
    }
    return 0;
}


HDU-5642 King's Order (DP)

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)


Problem Description
After the king's speech , everyone is encouraged. But the war is not over. The king needs to give orders from time to time. But sometimes he can not speak things well. So in his order there are some ones like this: "Let the group-p-p three come to me". As you can see letter 'p' repeats for 3 times. Poor king!

Now , it is war time , because of the spies from enemies , sometimes it is pretty hard for the general to tell which orders come from the king. But fortunately the general know how the king speaks: the king never repeats a letter for more than 3 times continually .And only this kind of order is legal. For example , the order: "Let the group-p-p-p three come to me" can never come from the king. While the order:" Let the group-p three come to me" is a legal statement.

The general wants to know how many legal orders that has the length of n

To make it simple , only lower case English Letters can appear in king's order , and please output the answer modulo  1000000007

We regard two strings are the same if and only if each charactor is the same place of these two strings are the same.
 

Input
The first line contains a number  T(T10) ——The number of the testcases.

For each testcase, the first line and the only line contains a positive number  n(n2000) .
 

Output
For each testcase, print a single number as the answer.
 

Sample Input
  
  
2 2 4
 

Sample Output
  
  
676 456950 hint: All the order that has length 2 are legal. So the answer is 26*26. For the order that has length 4. The illegal order are : "aaaa" , "bbbb"…….."zzzz" 26 orders in total. So the answer for n == 4 is 26^4-26 = 456950


题目大意:长度为n的字符指令(只有小写字母),若任意字符连续出现次数小于等于3次,则该指令合法,求所有长度为n且合法的指令数目,答案模1000000007

大致思路:看到这种求方案数的就应该想到DP,然后想如何转移即可


刚开始没有思路,感觉这次只能A 2题了(没想到最后真的只有2题...),便弃疗去玩游戏,不一会儿便想到一定是DP,最终找到了转移方程,然而WA了,又开始怀疑,想到容斥,数学方法也WA(其实开始就想到不可能,重复的情况太多),然后继续回来想转移方程,发现有一部分算重了,导致后面算的结果出现问题,改了后终于AC

看到题解说这题一眼就能看出来是数位DP,然而没学过(貌似只学过最简单的几种DP,其他都不知道...)

题解的复杂度是:O(26*26*n*T),我的这个复杂度是:O(26*n*T),看到有人用O(n)的方法直接预处理出所有答案(目瞪口呆)(貌似都可以预处理的...


O(n)的算法思路大致是:去掉26字母这一维,将所有的合并,dp[i][j]表示前i个字符,末尾有j个字符相同的方案数

状态转移方程:dp[i][1]=25*(dp[i-1][1]+dp[i-1][2]+dp[i-1][3]);//所有i-1字符指令的末尾加上不同于前一个字符的字符,共25个

dp[i][2]=dp[i-1][1];

dp[3]=dp[i-1][2];

#include <cstdio>
#include <cstring>

using namespace std;

const int mod=1000000007;

int T,n;
int dp[2005][27];

int main() {
    for(int j=0;j<26;++j)
        dp[1][j]=1;
    dp[0][26]=0;
    dp[1][26]=26;
    scanf("%d",&T);
    while(T--) {
        scanf("%d",&n);
        for(int i=2;i<=n;++i) {
            dp[i][26]=0;//为了减少计算,dp[i][26]表示前i个字符形成的所有合法方案数
            for(int j=0;j<26;++j) {
                dp[i][j]=dp[i-1][26];//dp[i][j]的方案数就是 n-1个字符形成的所有合法方案数 - 这些方案数中后面3个字符均为j的方案数
                if(i>3) {
                    dp[i][j]=(dp[i][j]-dp[i-3][j]+mod)%mod;//减去不合法的方案数(后面三个字符均为j时,其方案数就等于dp[i-3][j])
                    dp[i-2][j]=(dp[i-2][j]-dp[i-3][j]+mod)%mod;//由于从i-3开始均为j的方案数已经被排除,所以dp[i-2][j]也应该减去dp[i-3][j],即保证dp[i-2][j]的方案数均为末尾是j的方案数,第i-3个字符不是j,防止重复计算
                    dp[i-1][j]=(dp[i-1][j]-dp[i-3][j]+mod)%mod;
                }
                dp[i][26]=(dp[i][26]+dp[i][j])%mod;
            }
        }
        printf("%d\n",dp[n][26]);
    }
    return 0;
}

数位DP:(待完成)

——————————————————3题的旅游分割线———————————————————

HDU-5643 King's Game (递推)

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)


Problem Description
In order to remember history, King plans to play losephus problem in the parade gap.He calls  n(1n5000)  soldiers, counterclockwise in a circle, in label  1,2,3...n .

The first round, the first person with label  1  counts off, and the man who report number  1  is out.

The second round, the next person of the person who is out in the last round counts off, and the man who report number  2  is out.

The third round, the next person of the person who is out in the last round counts off, and the person who report number  3  is out.



The N - 1 round, the next person of the person who is out in the last round counts off, and the person who report number  n1  is out.

And the last man is survivor. Do you know the label of the survivor?
 

Input
The first line contains a number  T(0<T5000) , the number of the testcases.

For each test case, there are only one line, containing one integer  n , representing the number of players.
 

Output
Output exactly  T  lines. For each test case, print the label of the survivor.
 

Sample Input
  
  
2 2 3
 

Sample Output
  
  
2 2 Hint: For test case #1:the man who report number 1 is the man with label 1, so the man with label 2 is survivor. For test case #2:the man who report number 1 is the man with label 1, so the man with label 1 is out. Again the the man with label 2 counts 1, the man with label 3 counts 2, so the man who report number 2 is the man with label 3. At last the man with label 2 is survivor.

题目大意:n个人(编号为1,2,3……n),从第一个人开始数1,第一次数到1的人出去,下一个人从1开始重新数,第二次数到2的人出去……第n-1次,数到n-1的人出去,问最后留下的人的编号是多少。


知道如何用O(n)的方法求约瑟夫环,但是变形后就不知所措了,学得太死板了

发现好多人都是打表过的,感觉自己太年轻了,完全没想过打表


官方题解:

约瑟夫问题的一个变种,然而题目全部是在唬人,就是一个简单的递推。虽然我知道有人会打表。。。

我们看看裸的约瑟夫是怎么玩的:nn 个人,每隔 kk 个删除。

由于我们只关心最后一个被删除的人,并不关心最后的过程,所以,我们没有必要也不能够模拟整个过程。我们用递推解决。假设有nn个人围成环,标号为[0,n-1][0,n1]00开始的好处是取模方便),每数kk个人杀一个的情况下,最后一个存活的人的编号是f[n]f[n]

我们有f[1]=0f[1]=0,这不需要解释。

接着考虑一般情况f[n]f[n],第一个杀死的人的编号是k-1k1,杀死后只剩下n-1n1个人了,那么我们重新编号! http://blog.sengxian.com/solutions/_image/uva-1394/2.png

原来编号为k的现在是00号,也就是编号之间相差33我们只要知道现在n-1n1个人的情况最后是谁幸存也就知道nn个人的情况是谁幸存。幸运的是f[n-1]f[n1]已经算出来了那f[n]f[n]就是在f[n-1]f[n1]的基础上加上一个kk即可不要忘记总是要取模。

所以递推式子是: f[i] = \begin{cases} & \text{ 0 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ } i=1 \ & \text{ (f[i - 1] + k) mod i \ \ \ \ \ \ } other \end{cases}f[i]={ 0                      i=1 (f[i - 1] + k) mod i       other

此题只用在原版约瑟夫问题上加一维,由于依次隔 1, 2, 3...n - 11,2,3...n1 个人删除,所以用 f[i][j]f[i][j] 表示 ii 个人,依次隔 j, j + 1... j + i - 1j,j+1...j+i1 个人的幸存者标号。

根据刚才的重标号法,第一次 j - 1j1 号出局,从 jj 开始新的一轮,从 j + 1j+1 开始清除,剩余 i - 1i1 个人,也有递推式子:

f[i][j] = \begin{cases} & \text{ 0 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ } i=1 \ & \text{ (f[i - 1][j+1] + j) mod i \ \ \ \ \ \ } other \end{cases}f[i][j]={ 0                      i=1 (f[i - 1][j+1] + j) mod i       other

答案就是 f[n][1] + 1f[n][1]+1(将标号转移到 [1, n][1,n]),问题轻松解决。

复杂度:预处理 O(n^2)O(n2),查询 O(1)O(1),总复杂度 O(n^2)O(n2)。由于可以滚动数组以及常数太小,所以 nn 给了 50005000


#include <cstdio>
#include <cstring>

using namespace std;

const int mod=1000000007;

int T,n;
int dp[2][5005],ans[5005];

int main() {
    for(int j=0;j<=5000;++j)
        dp[1][j]=0;
    ans[1]=1;
    for(int i=2;i<=5000;++i) {
        for(int j=0;j<=5000;++j)
            dp[i&1][j]=(dp[(i+1)&1][j+1]+j)%i;
        ans[i]=dp[i&1][1]+1;
    }
    scanf("%d",&T);
    while(T--) {
        scanf("%d",&n);
        printf("%d\n",ans[n]);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值