Gym 101334(ACM ICPC 2005–2006, Northeastern European Regional Contest)

本文精选了几道典型的算法竞赛题目并提供了详细的解题思路及代码实现,包括区间查询、图论切割、动态规划等问题,适合算法学习者参考。

PROBLEM A D E F H I J ARE INCLUDED 

题目链接:https://odzkskevi.qnssl.com/cb170c7f26e08e74eda57aef92fbde52?v=1491702860

Problem A. Area 51

题意:

           给出N各点,求视角能按给定顺序看到各点的合法区间。

思路:

           可以发现,对于每一个点重合处,视角中点序会发生变化,其变化就是相应两点交换顺序。故可以从 -INF 开始模拟所有区间的视角,同时检查其是否符合题意。注意精度问题。

代码:

#include <bits/stdc++.h>

using namespace std;

typedef struct Node{
    char ch;
    double x,y;
    bool operator <(const Node &a)const{
        if(a.y==y){
            return a.x>x;
        }
        return a.y<y;
    }
}Node;
typedef struct Xpos{
    int x,y;
    double pos;
    bool operator <(const Xpos &a)const{
        if(pos==a.pos){
            if(x==a.x)
                return y>a.y;
            return x>a.x;
        }
        return a.pos>pos;
    }
}Xpos;
const int MAXN=105;
const double INF=1e8;
const double eps=1e-6;
char str[MAXN];
int n,tot,pos[MAXN],num[MAXN];
Node node[MAXN];
Xpos xpos[MAXN*MAXN];
double get(int i,int j){
    double x1=node[i].x,y1=node[i].y;
    double x2=node[j].x,y2=node[j].y;
    return x1-(x1-x2)/(y1-y2)*y1;
}
void getxpos(){
    tot=-1;
    for(int i=0;i<n;i++){
        for(int j=i+1;j<n;j++){
            if(node[i].y==node[j].y) continue;
            xpos[++tot].pos=get(i,j);
            xpos[tot].x=i;xpos[tot].y=j;
        }
    }
    sort(xpos,xpos+tot+1);
    Xpos end;end.pos=INF;end.x=end.y=0;
    xpos[++tot]=end;
    return ;
}
bool check(){
    for(int i=0;i<n;i++)
        if(node[num[i]].ch!=str[i]) return 0;
    return 1;
}
int main(){
    freopen("area.in","r",stdin);
    freopen("area.out","w",stdout);
    while(scanf("%d",&n)!=-1){
        getchar();
        scanf("%s",str);
        getchar();
        for(int i=0;i<n;i++){
            scanf("%c %lf %lf",&node[i].ch,&node[i].x,&node[i].y);
            getchar();
        }
        sort(node,node+n);
        getxpos();
        for(int i=0;i<n;i++){
            pos[i]=i;
            num[i]=i;
        }
        double st=-INF;
        vector <pair<double,double> >ans;
        for(int i=0;i<=tot;i++){
            if(xpos[i].pos-st>=eps&&check()){
                ans.push_back(make_pair(st,xpos[i].pos));
            }
            st=xpos[i].pos;
            if(xpos[i].x<xpos[i].y){
                swap(num[pos[xpos[i].x]],num[pos[xpos[i].y]]);
                swap(pos[xpos[i].x],pos[xpos[i].y]);
            }
        }
        int len=ans.size();
        printf("%d\n",len);
        for(int i=0;i<len;i++){
            double x=ans[i].first,y=ans[i].second;
            if(x==-INF) printf("* ");
            else printf("%.6lf ",x);
            if(y==INF) printf("*");
            else printf("%.6lf",y);
            if(i!=len-1) printf(" ");
            else printf("\n");
        }
        return 0;
    }
}
Problem D. Double Patience

题意:
           有9组牌,每组4张,两张牌的大小一样时可以消掉,每次只能消掉最上面的牌,然后一个在玩的时候如果有多种消法,就会随机选择其中一种,问他将所有牌都消掉的几率是多少。

思路:

           状压DP

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#define eps 1e-8

using namespace std;

int n5[10],a[10][5],b[10],w,num1,num2;
double dp[1953125];
string h;
vector<int> hh;

int main()
{
    freopen("double.in","r",stdin);
    freopen("double.out","w",stdout);
    for(int i=0;i<=9;i++)
        n5[i]=pow(5,i)+eps;
    for(int i=1;i<=9;i++)
    {
        for(int j=1;j<=4;j++)
        {
            cin>>h;
            a[i][j]=h[0];
        }
    }
    memset(dp,0,sizeof(dp));
    dp[1953124]=1;
    for(int i=1953124;i>=0;i--)
    {
        if(dp[i]==0)
            continue;
        w=i;
        for(int j=0;j<=8;j++)
        {
            b[j]=w%5;
            w/=5;
        }
        hh.clear();
        for(int j=0;j<=7;j++)
        {
            for(int k=j+1;k<=8;k++)
            {
                if(b[j]==0||b[k]==0)
                    continue;
                if(a[j+1][b[j]]!=a[k+1][b[k]])
                    continue;
                hh.push_back(i-n5[j]-n5[k]);
            }
        }
        w=hh.size();
        for(int j=0;j<w;j++)
            dp[hh[j]]+=dp[i]/w;
    }
    printf("%.6lf\n",dp[0]+eps);
    return 0;
}
Problem E. Exploring Pyramids

题意:

         给出一棵多叉树,每个结点的任意两个子结点都有左右之分。从根结点开始,每次尽量往左走,走不通了就回溯,把遇到的字母顺序记录下来,可以得到一个序列。如图所示的序列均为 ABABABA 。给定一个序列,问有多少棵树与之对应。

思路:

         DP

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#define mod 1000000000

using namespace std;

int main()
{
    freopen("exploring.in","r",stdin);
    freopen("exploring.out","w",stdout);
    string a;
    int dp[305][305],len;
    while(cin>>a)
    {
        memset(dp,0,sizeof(dp));
        len=a.size();
        if(len%2==0)
        {
            cout<<0<<endl;
            continue;
        }
        for(int i=0;i<len;i++)
            dp[i][i]=1;
        for(int i=2;i<len;i+=2)
        {
            for(int l=0;l<=len-i-1;l++)
            {
                int r=l+i;
                if(a[l]==a[r])
                {
                    dp[l][r]=dp[l+1][r-1];
                    for(int k=l+1;k<r;k++)
                        if(a[k]==a[l])
                            dp[l][r]=(dp[l][r]+(long long)dp[l+1][k-1]*dp[k][r])%mod;
                }
            }
        }
        cout<<dp[0][len-1]<<endl;
    }
    return 0;
}

Problem F. Feel Good

题意:
          给你一个序列,让你求其中一个子序列使得这个子序列和乘以这个子序列中最小值后最大。
思路:
          枚举每个位置,将这个位置视作为区间的最小值,找到左右方向比这个位置的值还小的位置。这样一来就确定了区间。
          区间的和利用前缀和维护即可。          
          找区间左右端点利用线段树查询即可。
代码:

#include <bits/stdc++.h>

using namespace std;
const int MAXN=1e6+100;
#define ls l,mid,rt*2
#define rs mid+1,r,rt*2+1
#define mi (l+r)/2
#define sf l,r,rt
const int INF=0x3f3f3f3f;
const long long inf=0x3f3f3f3f3f3f3f3f;
long long n,a[MAXN],minn[MAXN*4],sum[MAXN],ansl,ansr,v,posl,posr,st,en,ans1,ans2;
long long fans;
void push_up(int l,int r,int rt){
    minn[rt]=min(minn[rt*2],minn[rt*2+1]);
    return ;
}
void build(int l,int r,int rt){
    if(l==r){
        minn[rt]=a[l];
        return ;
    }
    int mid=mi;
    build(ls);
    build(rs);
    push_up(sf);
}
void queryl(int l,int r,int rt){
    if(st>r||en<l||posr!=n+1) return ;
    if(minn[rt]>=v) return ;
    if(l==r){
        ansl=minn[rt];
        posr=l;
        return ;
    }
    int mid=mi;
    queryl(ls);
    queryl(rs);
    return ;
}
void queryr(int l,int r,int rt){
    if(st>r||en<l||posl!=0) return ;
    if(minn[rt]>=v) return ;
    if(l==r){
        ansl=minn[rt];
        posl=r;
        return ;
    }
    int mid=mi;
    queryr(rs);
    queryr(ls);
    return ;
}
int main(){
    freopen("feelgood.in","r",stdin);
    freopen("feelgood.out","w",stdout);
    while(scanf("%d",&n)!=-1){
        fans=-INF;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            if(a[i]>fans){
                fans=a[i];
                ans1=ans2=i;
            }
        }
        sum[0]=0;
        for(int i=1;i<=n+1;i++)
            sum[i]=sum[i-1]+a[i];
        build(1,n,1);
        for(int i=1;i<=n;i++){
            v=a[i];
            ansl=INF;
            ansr=INF;
            st=i+1;
            en=n;
            posr=n+1;
            queryl(1,n,1);
            st=1;
            en=i-1;
            posl=0;
            queryr(1,n,1);
            long long temp=a[i]*(sum[posr-1]-sum[posl]);
            posl++;posr--;
            if(temp>fans){
                fans=temp;
                ans1=posl;
                ans2=posr;
            }
        }
        cout<<fans<<endl;
        cout<<ans1<<' '<<ans2<<endl;
    }
}


Problem H. Hardwood Cutting

题意:

        给出一个 N * M 的块,要将不同的区块切开,但只能从边缘切,问最终能切成几块。

思路:

        先把图转化一下将每个点分开,后将切点 push 入队。

        对于每个切点标记其四个方向,从四个方向进行图切。

        在切的过程中会产生新的可切点也 push 入队。

        显然的,当从一点开始的切线切到断点时,这条切线上的每个点都是新的切点。

        切好图后,dfs 统计联通块的个数即可。

代码:

//切图可以通过 debug() 查看

#include <bits/stdc++.h>

using namespace std;
typedef struct Node{
    int x,y;
    Node(int xx=0,int yy=0):x(xx),y(yy){};
}Node;
char maze[50][50];
bool vis[50][50][4],viss[50][50];
int n,m;
int dirx[]={0,-1,0,1};
int diry[]={-1,0,1,0};
queue <Node> que;
void debug(){
    for(int i=0;i<=n;i++){
        for(int j=0;j<=m;j++){
            printf("%c",maze[i][j]);
        }
        printf("\n");
    }
    puts(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
}
void ini(){
    n*=2;m*=2;
    memset(vis,0,sizeof(vis));
    memset(maze,0,sizeof(maze));
    memset(viss,0,sizeof(viss));
    getchar();
    for(int i=0;i<=n;i++){
        for(int j=0;j<=m;j++){
            if(i%2==0||j%2==0)
                maze[i][j]='#';
            else
                scanf("%c",&maze[i][j]);
        }
        if(i%2) getchar();
    }
    for(int i=0;i<=n;i++) maze[i][0]=maze[i][m]='$';
    for(int i=0;i<=m;i++) maze[0][i]=maze[n][i]='$';
    return ;
}
void cut(int x,int y,int dir){
    if(vis[x][y][dir]) return ;
    int stx=x,sty=y;
    int step=0;
    bool color=0;
    while(!color){
        vis[x][y][dir]=1;
        x+=dirx[dir];y+=diry[dir];
        if(x<0||y<0||x>n||y>m) break;
        step++;
        if(dir%2==0){
            if(maze[x-1][y]!=maze[x+1][y]){
                maze[x][y]='$';
                x+=dirx[dir];y+=diry[dir];
                if(maze[x][y]=='$'){
                    color=1;
                }else{
                    maze[x][y]='$';
                }
            }else{
                break;
            }
        }else{
            if(maze[x][y-1]!=maze[x][y+1]){
                maze[x][y]='$';
                x+=dirx[dir];y+=diry[dir];
                if(maze[x][y]=='$'){
                    color=1;
                }else{
                    maze[x][y]='$';
                }
            }else{
                break;
            }
        }
    }
    if(color){
        while(step--){
            stx+=2*dirx[dir];sty+=2*diry[dir];
            for(int i=0;i<4;i++)
            if(!vis[stx][sty][i]){
                que.push(Node(stx,sty));
                break;
            }
        }
    }
    return ;
}
void bfs(){
    while(!que.empty()) que.pop();
    for(int i=0;i<=n;i+=2){
        que.push(Node(i,0));
        que.push(Node(i,m));
    }
    for(int i=2;i<m;i+=2){
        que.push(Node(0,i));
        que.push(Node(n,i));
    }
    Node temp;
    while(!que.empty()){
        temp=que.front();que.pop();
        for(int i=0;i<4;i++)
            cut(temp.x,temp.y,i);
    }
}

void dfs(int x,int y){
    if(viss[x][y]) return ;
    viss[x][y]=1;
    for(int i=0;i<4;i++){
        if(maze[x+dirx[i]][y+diry[i]]!='$')
            dfs(x+dirx[i]*2,y+diry[i]*2);
    }
}
int solve(){
    int ans=0;
    for(int i=1;i<=n;i+=2){
        for(int j=1;j<=m;j+=2){
            if(!viss[i][j]){
                ans++;
                dfs(i,j);
            }
        }
    }
    return ans;
}
int main(){
    freopen("hardwood.in","r",stdin);
    freopen("hardwood.out","w",stdout);
    while(scanf("%d %d",&n,&m)!=-1){
        ini();
        bfs();
        //debug();
        cout<<solve()<<endl;
    }
}


Problem I. IP Networks

题意:

           给定一些IP地址,求出子网掩码和子网内最小IP。

思路:

           http://www.cnblogs.com/farewell-farewell/articles/5459415.html 

代码:

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

int main()
{
    freopen("ip.in","r",stdin);
    freopen("ip.out","w",stdout);
    int a,n,zw[40],num[40],s;
    while(cin>>n)
    {
        memset(num,-1,sizeof(num));
        for(int i=1;i<=32;i++)
            zw[i]=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=3;j++)
            {
                cin>>a;
                if(j!=3)
                    cin.get();
                for(int k=8;k>=1;k--)
                {
                    s=a%2;
                    a/=2;
                    if(num[j*8+k]==-1)
                        num[j*8+k]=s;
                    else if(zw[j*8+k]==1&&num[j*8+k]+s==1)
                    {
                        zw[j*8+k]=0;
                        num[j*8+k]=0;
                    }
                }
            }
        }
        for(int i=1;i<=32;i++)
        {
            if(zw[i]==0)
            {
                for(i=i;i<=32;i++)
                {
                    num[i]=0;
                    zw[i]=0;
                }
            }
        }
        for(int i=0;i<=3;i++)
        {
            a=0;
            for(int k=1;k<=8;k++)
            {
                a*=2;
                a+=num[i*8+k];
            }
            cout<<a;
            i==3?cout<<endl:cout<<".";
        }
        for(int i=0;i<=3;i++)
        {
            a=0;
            for(int k=1;k<=8;k++)
            {
                a*=2;
                a+=zw[i*8+k];
            }
            cout<<a;
            i==3?cout<<endl:cout<<".";
        }
    }
    return 0;
}



Problem J. Joseph’s Problem

摘自:扬帆起航

题意: 
          求给出的n,k的∑1<=i<=n(k mod i). 
思路:

          直接暴力的话肯定要超了,所以需要想办法缩减
          p  = k/i
          k mod i = k - p * i
          k mod ( i + 1 ) = k - p * ( i + 1 ) = k mod i - p
          k mod ( i + 2 ) = k - p * ( i + 2 ) = k mod i - 2 * p
          对于连续的 i ,很多p都是一样的 . 相差的部分是一个等差数列 ,
          i 的 范围是 从 i 到 min(k/p,n) 如果 p == 0 则 一直延续到最后

代码:

#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;

int main(int argc, const char * argv[]) {
   freopen("joseph.in","r", stdin);
   freopen("joseph.out", "w",stdout);
    long long n,k,sum1;
    while (scanf("%lld%lld",&n,&k)==2) {
        long long sum=0;long long s=0;
        long long kk=sqrt(k);
        if(n>kk){
            if(n<=k){
                s=k/n;
                long long d=(n-k/(s+1));
                sum+=((k%n)*2+(d-1)*s)*d/2;
            }
            for (int i=s+1; i<=kk; ++i) {
                long long d=(k/i-k/(i+1));
                sum+=((k%i)*2+(d-1)*i)*d/2;
            }
        }
        if(n>k)
            sum+=(n-k)*k;
        for (int i=2;i<=min(k/(kk+1),n); ++i) {
            sum+=k%i;
        }
        cout<<sum<<endl;
    }
    return 0;
}








内容概要:本文系统梳理了计算机组成原理的完整知识体系,重点围绕冯·诺依曼体系结构、五大核心硬件(运算器、控制器、存储器、输入/输出设备)、数据表示与运算、存储层级、CPU架构、总线系统及I/O控制机制展开,深入剖析了硬件底层如何支撑Java程序的运行,特别是与JVM内存模型、多线程并发、IO操作、性能调优等关键技术的紧密关联。文章强调通过理解二进制编码、补码运算、缓存机制、指令流水线、乱序执行、中断与DMA等底层原理,帮助Java开发者从根本上解释浮点精度丢失、数值溢出、线程安全、伪共享、IO阻塞等常见问题,并提供性能优化的硬件级视角。; 适合人群:具备一定Java开发基础,工作1-3年,希望深入理解JVM、并发编程、IO模型底层原理的后端研发人员。; 使用场景及目标:① 理解Java代码从编译到执行的完整软硬件链路;② 掌握JVM内存布局、GC机制、线程上下文切换、volatile与synchronized的硬件实现原理;③ 分析并解决高并发、高性能场景下的性能瓶颈,如缓存命中率低、伪共享、总线争用、IO阻塞等问题;④ 在面试中清晰阐述Java底层与计算机硬件的关联,展现扎实的系统功底。; 阅读建议:建议按照文档提供的“八、学习顺序”分阶段学习,每学完一个硬件模块,立即结合对应的Java技术点(如学到Cache时思考伪共享和@Contended)进行复盘,将抽象硬件原理具象化到日常开发场景中,以达到学以致用的目的。
源码链接: https://pan.quark.cn/s/044c82337b8e Teamcenter 12的中文版帮助文档中,1-1基础模块入门部分,详细介绍了产品设计、产品文档、产品结构、业务流程、客户端与集成接口、AWC、Rich client以及TC的安装与配置等内容,非常适合初学者或者希望了解Teamcenter 12的读者。Teamcenter 12是由Siemens开发的一款功能强大的产品生命周期管理(PLM)软件,它包含了产品设计、产品文档、产品结构、业务流程等多个领域,旨在帮助企业高效地管理产品开发的全过程。以下是对这些核心知识点的具体阐述: 1-1 Teamcenter简介: Teamcenter作为一个综合性的PLM解决方案,提供了一个集中化的平台来存储、管理和协同所有的产品相关信息。它支持跨部门、跨地域的合作,通过集成CAD系统、业务流程管理工具和数据管理功能,确保了数据的一致性和准确性。 1-1 产品设计: Teamcenter能够管理各种工程设计数据,包括3D模型、图纸、参数化数据等。用户可以方便地查找和组织产品设计数据,进行签出和签入操作以保证版本控制,确保在团队协作中避免数据冲突。 1-2 查找和组织产品设计: 在Teamcenter中,用户可以通过高级搜索功能迅速定位所需的设计文件,同时,系统支持自定义视图和分类,使设计数据的组织更加系统化。 1-3 签出和签入产品设计: 签出和签入机制是Teamcenter中防止数据冲突的重要功能。签出允许用户独占性地编辑某个设计文件,而签入则将更新后的文件版本提交回系统,供其他团队成员查看或编辑。 1-4 产品文档: Teamcenter不仅管理设计数据,还涵盖了产品相关的文档,如规格...
源码下载地址: https://pan.quark.cn/s/a4b39357ea24 在电磁模拟技术中,CST(Computer Simulation Technology)是一种被广泛采纳的软件工具,它主要用于电磁场、微波、天线以及射频系统的设计工作。本资料将详细分析CST软件中离散端口的具体配置方法,这些方法对于提升仿真结果的精确度和专业水准具有决定性作用。离散端口在CST软件中扮演着模拟信号输入或输出的重要角色,它们构成了仿真模型不可或缺的部分。在配置离散端口时,一个核心的原则是保证端口的方向与网格线保持一致,这是因为这样做能够有效降低计算过程中产生的误差,并确保仿真数据的有效性。如果未能遵循这一指导原则,可能会引发未知的计算问题,进而导致仿真结果失去可靠性。 在CST软件中配置离散端口,通常需要借助“Pick Points”这一功能。通过选择“Pick Edge Center”选项,端口将被设定在模型边缘的中心位置上。然而,这种做法并不总是能够确保端口与网格线保持平行。在某些特定情形下,模型的几何构造可能不允许直接选取一个与网格线平行的边作为端口的安装位置。 为了克服这一挑战,可以采用多种不同的策略。如果模型本身已经包含一条与馈电口平行的边,那么可以直接利用这条边来建立端口,此时CST软件会自动调整端口使其与网格线对齐。另一种可选的方法是,当模型不具备现成的平行边时,用户可以手动构建一个几何结构,比如一个立方体,并使其边缘与馈电口平行。通过这种方式,新建立的几何结构的边缘就可以作为端口的位置,从而确保端口与网格线的平行关系。 在实施上述操作时,必须关注端口尺寸的合理性和物理意义的一致性。端口的尺寸应当依据实际天线馈电部分的尺寸进行适当调整,过大的端口或...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值