Noip 2015 senior ,The semi-finals,analysis (复赛题解)

My English teacher says that my English is not enough, so forgive me for working hard to analyse today in English.
Today I finished noip 2015 years problems of senior, quite a feeling.ok,sorry,I have exceeded my top of capacity,so I alter to Chinese.
好的,今天我很earnest很earnest做了这套题,并且很earnest很earnest的写了变量名,我是really想学好English的。
The first 题目,simulation,模拟!我们可以do it with enthusiasm. 我就不说blather了,直接附上代码,你们可以imitate my style of creating the name of variable。

#include<iostream>
#include<cstdio>
using namespace std;
const int extend =40;
inline int read(){ 
    int data=0,w=1; 
    char ch=0; 
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar(); 
    if(ch=='-') w=-1,ch=getchar(); 
    while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar(); 
    return data*w;
}
int data_number,x,y,i,judge_standard;
int magic_square[extend][extend];
void solve(){
    while(i<data_number*data_number){  
        judge_standard = 0;  
        magic_square [x] [y] = ++ i;  
        if (x==1&&y!=data_number ) {judge_standard=1;x=data_number,y ++;}  
        if (y == data_number && x != 1 && judge_standard == 0) { judge_standard=2;y=1;x --; }  
        if (x == 1 && y == data_number && judge_standard == 0) { judge_standard=3;y=1;x=data_number;}  
        if (judge_standard == 0 ){x --,y ++;}  
        if (magic_square [x] [y])    
            switch ( judge_standard){  
                case 0 : x += 2,y --;break;  
                case 1 : x = 1; break;  
                case 2 : x ++; break;  
                case 3 : x=2,y=data_number;break;  
            }  
    }  
}
void lets_go_to_the_export(){
    for (int j=1;j<= data_number;j++){  
        for ( int k = 1 ; k <= data_number ; k ++ )  
            printf ( "%d " , magic_square[j][k] );  
        putchar ( '\n' );  
    }  
}
int main(){
    freopen("magic.in","r",stdin);
    freopen("magic.out","w",stdout);
    data_number=read();
    x=1,y=data_number/2+1,i=0;  
    solve();
    lets_go_to_the_export();
    return 0;
}

ok,the second 题目,读完题目过后,我们可以轻松distinguish这是一个ring。那么如何处理the smallest ring(草莽翻译),这是一个vital point。将题目translate一下,再形象的概括一下,最小环就相当于贪吃蛇,不断地elongate啊elongate,当chew到自己的body时,就结束。
那我们先一起来讲一讲最小环这个知识点吧。the smallest ring can be diveded 成两种class(类型),一种是有向的一种是无向的。先说有向吧,还是先说朴素算法。
令e(u,v)表示u和v之间的连边,再令min(u,v)表示,删除u和v之间的连边之后,u和v之间的最短路。最小环则是:min(u,v) + e(u,v),时间复杂度是EV2。其实都不用这种simple algorithm的,一般是用dijkstra或者是floyd。I recommend floyd,时间复杂度是O(n^3),不过如果大神会优先队列优化,那dijkstra不无不可。
https://vijos.org/p/1423,可以试试水,我来提供一个模板吧,O(∩_∩)O~。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 2000+10
#define INF 1000000
using namespace std;
int n,m,a,b,c,dist[maxn][maxn],ans=INF,t[maxn];
inline int read(){ 
    int data=0,w=1; 
    char ch=0; 
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar(); 
    if(ch=='-') w=-1,ch=getchar(); 
    while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar(); 
    return data*w;
}
int Min(int a,int b){
    if(a>=b) return b;
    else return a;
}
int main(){
    n=read();
    m=read();
    for(int i=1;i<=n;i++) t[i]=read();
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            dist[i][j]=dist[j][i]=INF;
    for(int i=1;i<=m;i++){
        a=read();b=read();c=read();
        dist[a][b]=Min(c+t[a],dist[a][b]);
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
    for(int i=2;i<=n;i++)     ans=min(ans,dist[1][i]+dist[i][1]);
    if(ans<INF) printf("%d",ans);
    else puts("-1");
    return 0;
}

then,无向图的最小环的求法不可能和有向图的求法一样, 因为在有向图中i 到j 和 j 到i 算是一个环,但在无向图中不是一个环,
如果直接用flody算法将会出错, 有向图的环可以为2个顶点,而无向图的环至少要三个顶点; 所以为了求无向图的最小环, 我们采用的principle是: 枚举最大环中的连接点,更新环的权重; 比普通Floyd多出来的部分,主要use到的原理是当处理到k时,所有以1 到k - 1为中间结点的最短路径都已经确定,则这时候的环为(i到j(1 < i, j <= k - 1)的最短路径) + 边(i, k) + 边(k, j)遍历所有的i, j找到上述式子的最小值即位k下的最小代价环 。you can try try。
now,我们来solve the message problem,我就把my thinking写在代码里面了哦。

#include<iostream>
#include<cstdio>
using namespace std;
const int extend =200010;
/*
不妨设每个点 i 有一条边连向点 t[i], 我们可以枚举从点 origination 
出发, 然后一直走下去, 并且每经过一个点就标记这个点. 
如果我们走到了一个已经标记了的点,此时有两种情况 
1. 这个点在我们从 s 出发走出的这条路径上, 这说明我们找到了一个环, 那么用这个环的大小更新答案即可; 
2. 否则, 我们已经处理过这个点了, 直接退出, 枚举下一个起点.
*/
inline int read(){ 
    int data=0,w=1; 
    char ch=0; 
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar(); 
    if(ch=='-') w=-1,ch=getchar(); 
    while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar(); 
    return data*w;
}
int Min(int a,int b){
    if(a>=b) return b;
    else return a;
}
int is_used[extend],times[extend];
int data_number,t[extend],ans = extend + 10000;
void deep_First_Search(int origination){
    int k = origination,auxliary = 0;
    while(true){
        times[origination] = auxliary++;
        is_used[origination] = k;
        origination = t[origination];
        if(is_used[origination] > 0){
            if(is_used[origination] == k){
                ans = Min(ans,auxliary - times[origination]);
                break;
            }
            else break;
        }
    }
}
void say_good_bye(int x){printf("%d",x);}
int main(){
    freopen("message.in","r",stdin);
    freopen("message.out","w",stdout);
    data_number=read();
    for(int i=1;i<=data_number;i++) t[i]=read();
    for(int i=1;i<=data_number;i++) if(is_used[i] == 0) deep_First_Search(i);
    say_good_bye(ans);
    return 0;
}

大概就这样吧,图论is very essential。
最后一道题,我think:Facing the hopeless situation you should never say never, go ahead cheat for the score instead.
我只能cheat了,╮(╯▽╰)╭。全力去骗30分,就是当牌只有2,3,4张时。永远没有顺子的情况。先看代码吧。

#include<iostream>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int data_number,n,Jack_card[20],receive1,receive2;
inline int read(){ 
    int data=0,w=1; 
    char ch=0; 
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar(); 
    if(ch=='-') w=-1,ch=getchar(); 
    while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar(); 
    return data*w;
}
void readIn1(){
    data_number=read();
    n=read();   
}
void readIn2(){
    for(int i=1;i<=n;i++){
        receive1=read();
        receive2=read();
        if(receive1==0){
            if(receive2==1) Jack_card[16]++;
            else Jack_card[17]++;
        }
        else if(receive1==1 || receive1==2) Jack_card[receive1+13]++;
        else Jack_card[receive1]++;
    }
}
void Memset(){for(int i=3;i<=17;i++) Jack_card[i]=0;}
void print(int x){printf("%d\n",x);}
void elaborator_cheat(){
    if(n==2){
        for(int i=3;i<=17;i++){
            if(Jack_card[i]==2 ||(Jack_card[16]==1 && Jack_card[17]==1) ){
                print(1);
                return ;
            }   
            if(Jack_card[i]==1){
                print(2);
                return ;
            }
        }
    }
    else if(n==3){
        for(int i=3;i<=17;i++){
            if(Jack_card[i]==3){
                print(1);
                return ;
            }   
            if(Jack_card[i]==2 ||(Jack_card[16]==1 && Jack_card[17]==1)){
                print(2);
                return ;
            }       
        }
        print(3);
        return ;
    }
    else if(n==4){
        int num_of_couple=0;
        if(Jack_card[16]==1 && Jack_card[17]==1) num_of_couple++;
        for(int i=3;i<=17;i++){
            if(Jack_card[i]==4 || Jack_card[i]==3){
                print(1);
                return ;
            }
            if(Jack_card[i]==2) num_of_couple++;

        }
        if(num_of_couple==0){
            print(4);
            return ;
        } 
        if(num_of_couple==1){
            print(3);
            return ;
        } 
        if(num_of_couple==2){
            print(2);
            return ;
        } 
    }
}
int main(){
    freopen("landlords.in","r",stdin);
    freopen("landlords.out","w",stdout);
    readIn1();
    while(data_number--){
        Memset();
        readIn2();
        elaborator_cheat();
    }
    return 0;
}

用尽毕生所学,枚举了2张,3张,4张的所有情况,I am very satisfied。
but now,我要学习一波正解,提高自己的代码及poker能力。
我research了一下big god的代码,都是优先搜索顺子,我思考了一下the reason,大概是有两个原因,一个是因为顺子最长,还有就是,只有顺子,会影响答案,所以搜索一下顺子的输出再剪枝。我的代码也许把你恶心到了,那下面这个代码,不是我写的了。想想吧。

#include<cstdio>
#include<cstring>
#include<climits>

int t,n,x,y,a[5],b[14],maxx;

int qiu()
{
    int tot=0;
    memset(a,0,sizeof(a));
    for(int i=0;i<=13;i++) a[b[i]]++;
    while(a[4] && a[2]>1) a[4]--,a[2]-=2,tot++;
    while(a[4] && a[1]>1) a[4]--,a[1]-=2,tot++;
    while(a[4] && a[2]) a[4]--,a[2]--,tot++;
    while(a[3] && a[2]) a[3]--,a[2]--,tot++;
    while(a[3] && a[1]) a[3]--,a[1]--,tot++;
    return tot+a[1]+a[2]+a[3]+a[4];
}

void dfs(int u)  //搜索顺子,二顺子,三顺子 
{
    if(u>=maxx) return;int kk=qiu();
    if(u+kk<maxx) maxx=u+kk;
    for(int i=2;i<=13;i++)
    {
        int j=i;
        while(b[j]>=3 && j<=13) j++;
        if(j-i>=2)
          for(int v=i+1;v<=j-1;v++)
          {

            for(int vk=i;vk<=v;vk++) b[vk]-=3;
            dfs(u+1);
            for(int vk=i;vk<=v;vk++) b[vk]+=3;
          }
    }
    for(int i=2;i<=13;i++)
    {
        int j=i;
        while(b[j]>=2 && j<=13) j++;
        if(j-i>=3)
          for(int v=i+2;v<=j-1;v++)
          {
            for(int vk=i;vk<=v;vk++) b[vk]-=2;
            dfs(u+1);
            for(int vk=i;vk<=v;vk++) b[vk]+=2;
          }
    }
    for(int i=2;i<=13;i++)
    {
        int j=i;
        while(b[j]>=1 && j<=13) j++;
        if(j-i>=5)
          for(int v=i+4;v<=j-1;v++)
          {
            for(int vk=i;vk<=v;vk++) b[vk]--;
            dfs(u+1);
            for(int vk=i;vk<=v;vk++) b[vk]++;
          }
    }
}

int main()
{
    scanf("%d%d",&t,&n);
    while(t--)
    {
        maxx=INT_MAX;
        memset(b,0,sizeof(b));
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&x,&y);
            if(x==1) x=13;
            else if(x) x--;
            b[x]++;
        }
        dfs(0);
        printf("%d\n",maxx);
    }
    return 0;
}


at last,我talk about 我这次考试的情况吧,(还没做day2呢),
the first: expect 100,in fact 100.
the second:expect 100,in fact 100.
the third :expect 30,in fact 30.
我去学习一下dijkstra ,我没有recommend it 就是因为 I have not control it。So。。。。。。。。。
Good Bye!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值