HFOI2017.07.11校内赛(普及组)题解

来源:互联网 发布:宏和网络 编辑:程序博客网 时间:2024/06/01 07:45
1:数字组合
描述
    有n个正整数,找出其中和为t(t也是正整数)的可能的组合方式。如:
    n=5,5个数分别为1,2,3,4,5,t=5;
    那么可能的组合有5=1+4和5=2+3和5=5三种组合方式。
输入
    输入的第一行是两个正整数n和t,用空格隔开,其中1<=n<=20,表示正整数的个数,t为要求的和(1<=t<=1000)
    接下来的一行是n个正整数,用空格隔开。
输出
    和为t的不同的组合方式的数目。
样例输入
    5 5
    1 2 3 4 5
样例输出

    3

令a(i)表示第i个正整数,f(i,j)表示前i个数,可以组成数字j的组数,则状态转移方程为:

if(j>a[i])f[i][j]=f[i-1][j]+f[i-1][j-a[i]];
if(j==a[i])f[i][j]=f[i-1][j]+1;
if(j<a[i])f[i][j]=f[i-1][j];

代码:

#include<cstdio>#include<cstring>using namespace std;int f[22][1005],a[22],n,k;int main(){    memset(f,0,sizeof f);    scanf("%d%d",&n,&k);    for(int i=1;i<=n;i++)scanf("%d",&a[i]);    for(int i=1;i<=n;i++)for(int j=0;j<=k;j++)    {   if(j>a[i])f[i][j]=f[i-1][j]+f[i-1][j-a[i]];    else if(j==a[i])f[i][j]=f[i-1][j]+1;    else f[i][j]=f[i-1][j];    }    printf("%d\n",f[n][k]);    return 0;}

T2:木棍加工
题目描述
一堆木头棍子共有n根,每根棍子的长度和宽度都是已知的。棍子可以被一台机器一个接一个地加工。机器处理一根棍子之前需要准备时间。准备时间是这样定义的:
第一根棍子的准备时间为1分钟;
如果刚处理完长度为L,宽度为W的棍子,那么如果下一个棍子长度为Li,宽度为Wi,并且满足L>=Li,W>=Wi,这个棍子就不需要准备时间,否则需要1分钟的准备时间;
计算处理完n根棍子所需要的最短准备时间。比如,你有5根棍子,长度和宽度分别为(4, 9),(5, 2),(2, 1),(3, 5),(1, 4),最短准备时间为2(按(4, 9)、(3, 5)、(1, 4)、(5, 2)、(2, 1)的次序进行加工)。
输入输出格式
输入格式:
第一行是一个整数n(n<=5000),第2行是2n个整数,分别是L1,W1,L2,w2,…,Ln,Wn。L和W的值均不超过10000,相邻两数之间用空格分开。
输出格式:
仅一行,一个整数,所需要的最短准备时间。
输入输出样例
输入样例#1:
5
4 9 5 2 2 1 3 5 1 4
输出样例#1:
2

dailworth定理:一个序列的不上升子序列的个数等于最长上升子序列的个数(自己想一想也能明白)

【有请zxj大神帮我们证明一下】

然后题目就转变为一个简单的DP问题了。

先对l进行排序,然后求r的不上升子序列的个数

#include<cstdio>#include<algorithm>#include<cstring>using namespace std;const int maxn=5005;struct Node{int x;int y;}node[maxn];bool cmp(Node a,Node b){    if(a.x<b.x)return 1;    else if(a.x==b.x&&a.y<b.y)return 1;    else return 0;}int main(){    int n,f[maxn],maxx=-1;    scanf("%d",&n);    for(int i=1;i<=n;i++)scanf("%d%d",&node[i].x,&node[i].y);    sort(node+1,node+1+n,cmp);    for(int i=0;i<maxn;i++)f[i]=1;    for(int i=2;i<=n;i++)for(int j=i-1;j>=1;j--)    if(node[i].y<node[j].y)f[i]=max(f[i],f[j]+1);    for(int i=1;i<=n;i++)if(f[i]>maxx)maxx=f[i];printf("%d\n",maxx);    return 0;}

3:词典
描述
    你旅游到了一个国外的城市。那里的人们说的外国语言你不能理解。不过幸运的是,你有一本词典可以帮助你。
输入
    首先输入一个词典,词典中包含不超过100000个词条,每个词条占据一行。每一个词条包括一个英文单词和一个外语单词,两个单词之间用一个空格隔开。而且在词典中不会有某个外语单词出现超过两次。词典之后是一个空行,然后给出一个由外语单词组成的文档,文档不超过100000行,而且每行只包括一个外语单词。输入中出现单词只包括小写字母,而且长度不会超过10。
输出
    在输出中,你需要把输入文档翻译成英文,每行输出一个英文单词。如果某个外语单词不在词典中,就把这个单词翻译成“eh”。
样例输入
    dog ogday
    cat atcay
    pig igpay
    froot ootfray
    loops oopslay

    atcay
    ittenkay
    oopslay
样例输出
    cat
    eh
    loops
提示
    输入比较大,推荐使用C语言的I / O函数。
来源
    翻译自Waterloo local 2001.09.22的试题 

很显然是Trie的题目(典型的用空间换时间)

好吧map也可以过(膜拜大神wzr)

具体实现如下:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=100010;char s[maxn][12];struct node{node*next[26];//26个子节点的指针int code;//对应的序号,中间节点或空节点为-1node(){code=-1;for(int i=0;i<26;i++)next[i]=NULL;}}head;//Trie的单个节点void insert(char *s,int v)//插入操作{node *now=&head;//从根节点开始for(int i=0;i<strlen(s);i++){if(now->next[s[i]-'a']==NULL)now->next[s[i]-'a']=new node;//如果节点不存在,创建一个新节点now=now->next[s[i]-'a'];//遍历到下一个节点}now->code=v;//给叶子节点存储信息}int query(char *s)//查询操作{node*now=&head;for(int i=0;i<strlen(s);i++){if(now->next[s[i]-'a']!=NULL)now=now->next[s[i]-'a'];//如果节点存在,继续遍历else return -1;//如果节点不存在,返回-1}return now->code;//返回叶子节点的值}int main(){memset(s,0,sizeof s);int cur=0;char a[12],q[12];while(1){scanf("%s",s[cur++]);char c=getchar();if(c=='\n')break;scanf("%s",a);insert(a,cur-1);}if(query(s[cur-1])==-1)printf("eh\n");else printf("%s\n",s[query(s[cur-1])]);//输入非常麻烦while(scanf("%s",q)==1){if(query(q)==-1)printf("eh\n");else printf("%s\n",s[query(q)]);}return 0;}