2014-2015 ACM-ICPC East Central North America Regional Contest (ECNA 2014)

训练感想

照惯例,先膜拜两位大神队友 @tomriddly@jiefangxuanyan
今天是World Final的日子。。训练的时候老想着看直播。。状态不佳。。
不过还好,最后把卡住的题都写出来了,值得总结一下。


题目分析

惯例,先是题目,然后是Gym地址。

A题 Continued Fraction

题意

给定连分数的定义,输入两个连分数,输出这两个分数的四则运算结果的连分数形式。
直接上分数类,先把一个连分数转化成一个正常分数,计算之后再转换回连分数。
这道题要注意,计算差的时候可能是负数 ,这时候把a0处理成负数就可以了。

/* written by jiefangxuanyan */
#include <cstdio>
#include <algorithm>
typedef long long LL;
LL gcd(LL a,LL b){
    return a?gcd(b%a,a):b;
}
struct fr{
    LL a,b;
    fr(LL aa,LL bb){
        LL g=gcd(aa,bb);
        a=aa/g;
        b=bb/g;
    }
    fr operator+(const fr &x){
        return fr(a*x.b+x.a*b,b*x.b);
    }
    fr operator-(const fr &x){
        return fr(a*x.b-x.a*b,b*x.b);
    }
    fr operator*(const fr &x){
        return fr(a*x.a,b*x.b);
    }
    fr operator/(const fr &x){
        return fr(a*x.b,b*x.a);
    }
    void add(LL x){
        a+=b*x;
    }
    LL split(){
        LL r=a/b;
        a%=b;
        if(a<0){
            r--;
            a+=b;
        }
        return r;
    }
    void inv(){
        std::swap(a,b);
        if(b<0){
            a=-a;
            b=-b;
        }
    }
    operator bool(){
        return a;
    }
};
const int N=20;
LL in[N];
int main(){
    int cn=0;
    while(true){
        int n,m;
        scanf("%d%d",&n,&m);
        if(!(n|m)){
            return 0;
        }
        for(int i=0;i<n;i++){
            scanf("%I64d",in+i);
        }
        fr x(0,1);
        for(int i=n-1;i>=0;i--){
            x.add(in[i]);
            x.inv();
        }
        x.inv();
        for(int i=0;i<m;i++){
            scanf("%I64d",in+i);
        }
        fr y(0,1);
        for(int i=m-1;i>=0;i--){
            y.add(in[i]);
            y.inv();
        }
        y.inv();
        fr add=x+y,sub=x-y,mul=x*y,div=x/y;
        printf("Case %d:\n",++cn);
        add.inv();
        while(add){
            add.inv();
            printf("%I64d",add.split());
            if(add){
                putchar(' ');
            }
        }
        putchar('\n');
        sub.inv();
        while(sub){
            sub.inv();
            printf("%I64d",sub.split());
            if(sub){
                putchar(' ');
            }
        }
        putchar('\n');
        mul.inv();
        while(mul){
            mul.inv();
            printf("%I64d",mul.split());
            if(mul){
                putchar(' ');
            }
        }
        putchar('\n');
        div.inv();
        while(div){
            div.inv();
            printf("%I64d",div.split());
            if(div){
                putchar(' ');
            }
        }
        putchar('\n');
    }
}

B题 A Cure for the Common Code

题意

给一个字符串,大概是abcbcbcbca这种,我们可以把他变成a4(bc)a这样。前面的字符串长度是11,而后面的只有7(算上括号),问给定一个字符串,能把他变成的最短长度是多少。
t[l][r]记录l到r之间的字符串所能折叠的最短长度,然后进行记忆化搜索。状态转移的时候要枚举两部分,一部分是只把字符串分成两部分,另外一部分是要把字符串中的循环节找出来进行更新。

/* written by jiefangxuanyan */
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N=550;
int t[N][N];
char in[N];
int numLen(int x){
    int s=0;
    while(x){
        x/=10;
        s++;
    }
    return s;
}
int dfs(int l,int r){
    int len=r-l;
    if(len<=1){
        return len;
    }
    if(t[l][r]!=-1){
        return t[l][r];
    }
    int s=0x3fffffff;
    for(int m=l+1;m<r;m++){
        s=std::min(dfs(l,m)+dfs(m,r),s);
    }
    for(int lp=1;lp<len;lp++){
        if(len%lp==0){
            int seg=len/lp;
            for(int begin=l;begin<l+lp;begin++){
                for(int st=1;st<seg;st++){
                    if(in[begin]!=in[begin+st*lp]){
                        goto lblNoneLoop;
                    }
                }
            }
            s=std::min(dfs(l,l+lp)+(lp>1?2:0)+numLen(seg),s);
        lblNoneLoop:
            ;
        }
    }
    //printf("%d,%d:%d\n",l,r,s);
    t[l][r]=s;
    return s;
}
int main(){
    int cn=0;
    while(true){
        gets(in);
        if(strcmp(in,"0")==0){
            return 0;
        }
        int n=strlen(in);
        memset(t,-1,sizeof(t));
        printf("Case %d: %d\n",++cn,dfs(0,n));
    }
}

C题 Domiyahtzee!

题意

找安神的意思,应该是一道大型(中型)模拟,但是写到一半没写完。
等写出来了再看看怎么回事吧。。。


D题 Generalized Roman Numerals

题意

给定一个罗马数字的串的定义,定义是递归的。当左半串代表的数比右半串大的时候,串的值是左半加右半,反之,值为右半减左半。
比如IVX可以为4,也可以为6
给定一个串,问能够代表哪些数字。
搜索并且记忆化。注意这里需要在每个状态下记录这个串的可能的数值。我们用一个vector<int>去记录。刚开始的时候安神对这个容器进行排序去重,超时了。然后东神用了一个bool has[N][N][5500]解决了这个问题。这里用hash来判定的确要快一些,至于如何实现,我个人认为这种方式就很不错了。如果安神的unorder_set调试成功也是可以的。。

/* written by jiefangxuanyan */
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
const int N=55;
std::vector<int> t[N][N];
bool has[N][N][5500];
char in[N];
int ch2n(char ch){
    return ch=='I'?1:ch=='V'?5:ch=='X'?10:ch=='L'?50:ch=='C'?100:0;
}
void dfs(int l,int r,std::vector<int> &vr){
    if(!vr.empty()){
        return;
    }
    if(r-l==1){
        vr.push_back(ch2n(in[l]));
        return;
    }
    for(int m=l+1;m<r;m++){
        dfs(l,m,t[l][m]);
        dfs(m,r,t[m][r]);
        for(int i=0;i<t[l][m].size();i++){
            for(int j=0;j<t[m][r].size();j++){
                int a=t[l][m][i],b=t[m][r][j];
                int cur=a<b?b-a:a+b;
                if(!has[l][r][cur]){
                    has[l][r][cur]=true;
                    vr.push_back(cur);
                }
            }
        }
    }
}
int main(){
    int cn=0;
    while(true){
        gets(in);
        if(strcmp(in,"0")==0){
            return 0;
        }
        int n=strlen(in);
        for(int i=0;i<n;i++){
            for(int j=i+1;j<=n;j++){
                t[i][j].clear();
            }
        }
        memset(has,0,sizeof(has));
        dfs(0,n,t[0][n]);
        printf("Case %d:",++cn);
        std::sort(t[0][n].begin(),t[0][n].end());
        for(int i=0;i<t[0][n].size();i++){
            printf(" %d",t[0][n][i]);
        }
        putchar('\n');
    }
}

E题 Inspectors

题意

没咋看。。俩队友就给过了。。。。求队友解释题意。。
貌似是二分图带权匹配 用了kuangbin的大模板里的KM
敲模板一定要检查一遍!

/* written by jiefangxuanyan */
#include <cstdio>
#include <cstring>
const int N=110;
const int INF=0x3f3f3f3f;
int nx,ny;
int g[N][N];
int linker[N],lx[N],ly[N];
int slack[N];
bool visx[N],visy[N];
bool DFS(int x){
    visx[x]=true;
    for(int y=0;y<ny;y++){
        if(visy[y]){
            continue;
        }
        int tmp=lx[x]+ly[y]-g[x][y];
        if(tmp==0){
            visy[y]=true;
            if(linker[y]==-1||DFS(linker[y])){
                linker[y]=x;
                return true;
            }
        } else if(slack[y]>tmp){
            slack[y]=tmp;
        }
    }
    return false;
}
int KM(){
    memset(linker,-1,sizeof(linker));
    memset(ly,0,sizeof(ly));
    for(int i=0;i<nx;i++){
        lx[i]=-INF;
        for(int j=0;j<ny;j++){
            if(g[i][j]>lx[i]){
                lx[i]=g[i][j];
            }
        }
    }
    for(int x=0;x<nx;x++){
        for(int i=0;i<ny;i++){
            slack[i]=INF;
        }
        while(true){
            memset(visx,false,sizeof(visx));
            memset(visy,false,sizeof(visy));
            if(DFS(x)){
                break;
            }
            int d=INF;
            for(int i=0;i<ny;i++){
                if(!visy[i]&&d>slack[i]){
                    d=slack[i];
                }
            }
            for(int i=0;i<nx;i++){
                if(visx[i]){
                    lx[i]-=d;
                }
            }
            for(int i=0;i<ny;i++){
                if(visy[i]){
                    ly[i]+=d;
                } else{
                    slack[i]-=d;
                }
            }
        }
    }
    int res=0;
    for(int i=0;i<ny;i++){
        if(linker[i]!=-1){
            res+=g[linker[i]][i];
        }
    }
    return res;
}
int main(){
    int cn;
    scanf("%d",&cn);
    for(int ci=1;ci<=cn;ci++){
        scanf("%d",&nx);
        ny=nx;
        memset(g,-0x3f,sizeof(g));
        for(int i=0;i<nx;i++){
            for(int j=i+1;j<ny;j++){
                scanf("%d",g[i]+j);
                g[i][j]=-g[i][j];
                g[j][i]=g[i][j];
            }
        }
        printf("Case %d: %d\n",ci,-KM());
    }
    return 0;
}

F题 Path of Least Persistence

题意

给一个n*m的地图,每个格子上面有一个方向和长度。只能按照约束走。要求改变一个格子上的方向和长度,使得从(1,1)走到end的跳跃次数最少。
也用了一个dp的思想,貌似有些细节问题。最后一分钟过的你敢信!

/* written by jiefangxuanyan */
#include <cstdio>
#include <cstring>
//#include <iostream>
#include <algorithm>
const int N=110;
int d[N][N];
int dx(char ch){
    return ch=='N'?-1:ch=='S'?1:ch=='E'?0:0;
}
int dy(char ch){
    return ch=='N'?0:ch=='S'?0:ch=='E'?1:-1;
}
char op[N][N];
int jmp[N][N];
int n,m;
bool valid(int x,int y){
    return 0<=x&&x<n&&0<=y&&y<m;
}
int dfs(int x,int y){
    if(!valid(x,y)){
        return 0x3fffffff;
    }
    if(d[x][y]!=-1){
        return d[x][y];
    }
    if(x==n-1&&y==m-1){
        d[x][y]=0;
        return 0;
    }
    d[x][y]=0x3fffffff;
    d[x][y]=dfs(x+jmp[x][y]*dx(op[x][y]),y+jmp[x][y]*dy(op[x][y]))+1;
    return d[x][y];
}
const int xx[]={-1,1,0,0},yy[]={0,0,-1,1};
int lastx,lasty,lastjmp;
char lastop;
struct J{
    int d,x,y,jmp;
    char op;
    J(int d,int x,int y,int jmp,char op):d(d),x(x),y(y),jmp(jmp),op(op){}
    bool operator<(const J &fuck)const{
        if(d!=fuck.d){
            return d<fuck.d;
        }
        if(op=='X'&&fuck.op=='X'){
            return false;
        }
        if(op=='X'&&fuck.op!='X'||op!='X'&&fuck.op=='X'){
            return op=='X';
        }
        if(x!=fuck.x){
            return x<fuck.x;
        }
        if(y!=fuck.y){
            return y<fuck.y;
        }
        if(jmp!=fuck.jmp){
            return jmp<fuck.jmp;
        }
        return op<fuck.op;
    }
};
bool dfs2vis[N][N];
J dfs2(int x,int y){
    if(!valid(x,y)||dfs2vis[x][y]){
        return J(0x3fffffff,0,0,0,'X');
    }
    dfs2vis[x][y]=true;
    if(x==n-1&&y==m-1){
        return J(0,0,0,0,'X');
    }
    J r(0x3fffffff,0,0,0,'X');
    for(int dir=0;dir<4;dir++){
        for(int st=1;valid(x+xx[dir]*st,y+yy[dir]*st);st++){
            r=std::min(J(dfs(x+xx[dir]*st,y+yy[dir]*st)+1,x,y,st,"NSWE"[dir]),r);
        }
    }
    J last=dfs2(x+jmp[x][y]*dx(op[x][y]),y+jmp[x][y]*dy(op[x][y]));
    last.d++;
    return std::min(last,r);
}
int main(){
    int cn=0;
    while(true){
        scanf("%d%d",&n,&m);
        if(!(n|m)){
            return 0;
        }
        for(int x=0;x<n;x++){
            for(int y=0;y<m;y++){
                if(!(x==n-1&&y==m-1)){
                    scanf("%d%c",jmp[x]+y,op[x]+y);
                }
            }
        }
        memset(d,-1,sizeof(d));
        memset(dfs2vis,0,sizeof(dfs2vis));
        J r=dfs2(0,0);
        printf("Case %d: ",++cn);
        if(r.d>=0x3fffffff){
            puts("impossible");
        } else if(r.op=='X'){
            printf("none %d\n",r.d);
        } else {
            printf("%d %d %d%c %d\n",r.x,r.y,r.jmp,r.op,r.d);
        }
    }
    return 0;
}

G题 Speed Skills

题意

还不会、、先占个坑。


H题 Time Warp

题意

这题也坑了我们好久。。题意就不解释了,刚开始的时候东神寻思直接把表打出来然后再算,然后发现精度有很大的问题。。调了很久都没过,直接打乱了节奏。最后我想出了用追及问题的想法来写。。我简直是个小学生。。。不过过题就好辣!

/*************************************************************************
    > File Name: ph.cpp
    > Author: znl1087
    > Mail: loveCJNforever@gmail.com 
    > Created Time: 三  5/20 18:39:24 2015
 ************************************************************************/

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
void print(int se){
    se = (se +43200)%43200;
    int h = se / 3600 +1;
    se %= 3600;
    int m = se / 60;
    int s = se%60;
    printf("%d:%02d:%02d\n",h,m,s);
}

int main(){
    int ca;
    cin>>ca;
    int a,b;
    char op[10];
    for(int cas = 1 ; cas <= ca ;cas++){
        scanf("%d %s %d",&a, op ,&b);
        printf("Case %d: ",cas);
        if( op[0] == 'a'){
            int have = (12 - b) * 30;
            double need = (double)(a > have? a - have:360 + a - have);
            int ans = (int)round(need / (11.0/120.0));
            print((b-1) * 3600 + ans);
        }else if(op[0] == 't'){
            int have = (12 - b) * 30;
            double need = (double)(a < have? have -a: 360 + have -a);
            int ans = (int)round(need / (11.0/120.0));
            print((b-1) * 3600 - ans);
        }
    }
    return 0;
}

I题 Watch, Man!

题意

不会,占坑待填。。貌似是个计算几何?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值