NOIP2008题解

10 篇文章 0 订阅
5 篇文章 0 订阅

word:
题目大意:给你一个仅由小写字母构成的字符串,求这个字符串中出现次数最多的字符的出现次数减出现次数最少的字符的出现次数是否为一个质数。字符串长度<=100.
题解:
暴力枚举每个字母出现了多少次,再用最大的减最小的即可,注意出现次数为0的不能更新最小值。然后判断是否是个质数,注意特判0和1.时间复杂度:O(len),空间复杂度:O(26)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
int n,i,a[110],maxn,minn=1000;
char s[110];
int main(){
    freopen("word.in","r",stdin);
    freopen("word.out","w",stdout);
    scanf("%s",s);
    n=strlen(s);
    for(i=0;i<n;i++)a[s[i]-'a']++;
    for(i=0;i<27;i++){
        if(maxn<a[i])maxn=a[i];
        if(minn>a[i]&&a[i])minn=a[i];
    }
    if(maxn-minn==2){
        puts("Lucky Word");
        puts("2");
        return 0;
    }
    if(maxn-minn<2){
        puts("No Answer");
        puts("0");
        return 0;
    }
    for(i=2;i<maxn-minn;i++)
        if(!((maxn-minn)%i)){
            puts("No Answer");
            puts("0");
            return 0;
        }
    puts("Lucky Word");
    printf("%d",maxn-minn);
    return 0;
}

matches:
题目大意:有N根火柴棒,求能组成多少个A+B=C。(N<=24)
题解:先将0~9所需的火柴棒根数打表,然后处理所有数所需的火柴棒根数,然后再暴力求出所有能满足的等式。时间复杂度:O(能过),空间复杂度:O(能用火柴拼出的数)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
int a[4010]={6,2,5,5,4,5,6,3,7,6},i,n,j,ans;
int main(){
    freopen("matches.in","r",stdin);
    freopen("matches.out","w",stdout);
    scanf("%d",&n);
    n-=4;
    for(i=10;i<=4000;i++)a[i]=a[i/10]+a[i%10];
    for(i=0;i<=2000;i++)
        for(j=0;j<=2000;j++)
            if(a[i]+a[j]+a[i+j]==n)ans++;
    printf("%d",ans);
    return 0;
}

message:
题目大意:有一个m*n的矩阵,每个格子上有一个数值,现在要从左上角走到右下角再走回来,同一格子不能重复经过两次(除了左上角的格子),求数值的最大和。(n,m<=50)
题解:
倘若每个格子可以重复走,那么是一个很简单的DP题,设f[i][j]为从(1,1)开始走的数值最大和,那么f[i][j]=max(f[i-1][j],f[i][j-1])+a[i][j],最终答案为f[n][m]*2.
而现在不能重复走了,那我们重复走了的地方赋值为0就好了嘛……也就是设f[i][j][k][l]为从(1,1)开始走的两条路,一条到了(i,j),一条到了(k,l),(空间上是还可以再压一维的,但是现在能过就没写了,可以自己思索一下)那么f[i][j][k][l]=max(f[i-1][j][k-1][l],f[i-1][j][k][l-1],f[i][j-1][k-1][l],f[i][j-1][k][l-1])+a[i][j]+a[k][l],if i==k&&j==l then f[i][j][k][l]-=a[i][j]。
证明:若最大答案为走了重复地方的两条路,则至少有一个地方获得的数值为0,∵所有数值均为正数,∴必定存在另一条路使得f[i][j][k][l]的值更大。
时间复杂度:O(n^2*m^2),空间复杂度:O(n^2*m^2)或O(n^2*m)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
int n,m,f[60][60][60][60],i,j,k,l,a[60][60];
int main(){
    freopen("message.in","r",stdin);
    freopen("message.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)scanf("%d",&a[i][j]);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            for(k=1;k<=n;k++)
                for(l=1;l<=m;l++){
                    f[i][j][k][l]=max(f[i-1][j][k-1][l],max(f[i][j-1][k-1][l],max(f[i-1][j][k][l-1],f[i][j-1][k][l-1])))+a[i][j]+a[k][l];
                    if(i==k&&j==l)f[i][j][k][l]-=a[i][j];
                }

    printf("%d",f[n][m][n][m]);
    return 0;
}

twostack:
题目大意:给你一个n的排列,两个栈,输出一种方案使得这n个数从小到大输出,优先使用第一个栈,如果无法从小到大输出则输出0.
题解:
这题是一道构建二分图的题,两个栈即为二分图的两边(关于二分图的定义等等可以去网上找).
而构建二分图的一个比较通用的方法是染色,那么我们现在只需要在优先选择第一个栈的时候考虑是否能满足二分图即可。
那么现在就是考虑什么数必须放在二分图的两边。
若i

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
int n,i,j,a[1010],f[1010],w,colo[1010];
stack<int> s1,s2;
bool flag[1010][1010],ff;
void dfs(int x,int col){
    colo[x]=col;
    for(int i=1;i<=n;i++)
        if(flag[i][x]){
            if(colo[i]==col){
                puts("0");
                exit(0);
            }
            if(!colo[i])dfs(i,3-col);
        }
}
int main(){
    freopen("twostack.in","r",stdin);
    freopen("twostack.out","w",stdout);
    scanf("%d",&n);
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    f[n+1]=1001;
    for(i=n;i;i--)f[i]=min(a[i],f[i+1]);
    for(i=1;i<n;i++)
        for(j=i+1;j<=n;j++)
            if(a[i]<a[j]&&f[j]<a[i])flag[i][j]=flag[j][i]=1;
    for(i=1;i<=n;i++)if(!colo[i])dfs(i,1);
    j=1;
    for(i=1;i<=n;i++){
        if(colo[i]==1){
            putchar('a'),putchar(' ');
            s1.push(a[i]);
        }
        else{
            putchar('c'),putchar(' ');
            s2.push(a[i]);
        }
        ff=1;
        while(ff){
            ff=0;
            if(!s1.empty())
                if(s1.top()==j)s1.pop(),putchar('b'),putchar(' '),j++,ff=1;
            if(!s2.empty())
                if(s2.top()==j)s2.pop(),putchar('d'),putchar(' '),j++,ff=1;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值