tEST 1 for NOIP 2017.9.9.

来源:互联网 发布:淘宝新开店铺 编辑:程序博客网 时间:2024/06/07 01:06

头更大

这个9月完就要去集训搞NOIP了。。。

9月30天也就3次测试。。。为防万一我还是每次测试玩都写个总结。。


补进度来的,上周有点浪。
第一题苟住,第二题第三题GG

Function(100/100)

题目背景
SOURCE:NOIP2015-SHY-6

题目描述
对于一个整数,定义 f(x) 为他的每个数位的阶乘的乘积。例如 f(135)=1! * 3! * 5! = 720。给出一个数 a(可以包含前缀零),a 满足他的至少一个数位大于 1 。我们要求出最大的整数 x ,其中 x 不含 0 或 1 ,并且满足 f(a) = f(x)。

输入格式
第一行一个整数 n ,表示 a 的长度。
接下来一个整数 a 。

输出格式
一行一个整数 x 表示答案。

样例数据 1
输入  [复制]

4
1234
输出

33222
样例数据 2
输入  [复制]

2
03
输出

3
备注
【样例1说明】
1! * 2! * 3! * 4! = 3! * 3! * 2! * 2! * 2!

【样例2说明】
0! * 3! = 3!

【数据范围】
对 30% 的输入数据 :n≤2
对 100% 的输入数据 :n≤15

暴力枚举多想即可。

MY/STD.CPP

#include<iostream>#include<iomanip>#include<cstdio>#include<cstdlib>#include<cstring>#include<cmath>#include<string>#include<algorithm>#include<queue>using namespace std;int n,stk[60];long long hh;int cnt = 0;void get(int n){    switch(n)    {        case 2:            stk[++cnt]=2;            break;        case 3:            stk[++cnt]=3;            break;        case 4:            stk[++cnt]=2;stk[++cnt]=2;stk[++cnt]=3;            break;        case 5:            stk[++cnt]=5;            break;        case 6:            stk[++cnt]=3;stk[++cnt]=5;            break;        case 7:            stk[++cnt]=7;            break;        case 8:            stk[++cnt]=2;stk[++cnt]=2;stk[++cnt]=2;stk[++cnt]=7;            break;        case 9:            stk[++cnt]=2;stk[++cnt]=3;stk[++cnt]=3;stk[++cnt]=7;                    break;    }}int main(){    cin >> n;    cin >> hh;    while(hh)    {        int jud = hh%10;        if(jud!=0&&jud!=1)get(jud);        hh/=10;    }    sort(stk+1,stk+cnt+1);    for(int i=cnt;i>=1;i--)cout<<stk[i];    return 0;}

Box

题目背景
SOURCE:NOIP2015-SHY-6

题目描述
有个桌子长 R 宽 C ,被分为 R*C 个小方格。其中,一些方格上有箱子,一些方格上有按钮,一些方格上有障碍物,一些方格上是空地。现在有个任务,需要把所有箱子推到这些按钮上面。箱子有个特征,只能推不能搬不能拉。现在需要用最少的步数把所有箱子推到按钮上。

当然,箱子和人都只能以格子为单位移动,不存在一部分在格内一部分在格外的情况;只能向四个方向推,箱子和推箱子的队员都不能穿越障碍物。推动的定义是,人的前进方向被箱子挡住,且箱子在顺着前进方向的下一格不是箱子或者障碍物,那么就可以推着箱子和箱子一起前进一步。

输入格式
输入第一行有两个整数 R,C(4<=R,C<=7),代表桌子的长和宽,接下来一共有 R 行,每行有 C 个字符。
字符的含义:
0:代表空地
1:代表障碍物
2:代表箱子
3:代表按钮
4:人所在的初始地方
输入数据保证障碍物环绕整个地图,箱子数目跟按钮数目相同,箱子最少有一个,不会超过 3 个,队员肯定只有一个。不用考虑箱子初始就按着按钮的情况。保证有解。

输出格式
输出只有一行,为解决机关的最小步数。

样例数据 1
输入  [复制]

4 5
11111
14231
10231
11111
输出

4
备注
【样例说明】
在样例中,最快的方法之一是先向东把第一个箱子推到按钮上,然后向西走回原处,之后向南一步,再向东推第二个箱子到按钮上。至此任务完成,共使用了 4 步。

【数据范围】
对 30% 的输入数据 :箱子只有一个。
对 100% 的输入数据 :4<=R,C<=7。

一开始我怎么也觉得不可能用Dp应该用搜索才可能搞定像推箱子这样的题。。后来发现我错的离谱。。Dp大法好。。
虽然我到现在都还没看怎么懂orz。

STD.CPP

#include<iostream> #include<algorithm>#include<cstdio>#include<cstdlib>#include<cstring>#include<string>#include<cmath>#include<ctime>#include<cctype>#include<queue>using namespace std;int D[5][3],len,a[4],R[64][3],v[4],tot,n,m,A[8][8],f[50][50][50][50],pos[7][7],c[6000001][4];bool b[9][9];char sl[9];int main(){    //freopen("box.in","r",stdin);     //freopen("box.out","w",stdout);    cin>>n>>m;    int sx,sy;    D[1][1]=1;    D[1][2]=0;    D[2][1]=-1;    D[2][2]=0;    D[3][1]=0;    D[3][2]=1;    D[4][1]=0;    D[4][2]=-1;    len=0;    tot=0;    int cnt=0;    memset(R,0,sizeof(R));    memset(a,0,sizeof(a));    memset(v,0,sizeof(v));    for(int i=1;i<=n;i++)    {        scanf("%s",sl);        for(int j=1;j<=m;j++)        {            pos[i][j]=++cnt;            R[cnt][1]=i;            R[cnt][2]=j;            A[i][j]=sl[j-1]-'0';            if(A[i][j]==4) sx=i,sy=j,A[i][j]=0;            if(A[i][j]==2) a[++len]=pos[i][j];            if(A[i][j]==3) v[++tot]=pos[i][j];        }    }    memset(f,255,sizeof(f));    f[pos[sx][sy]][a[1]][a[2]][a[3]]=0;    c[1][0]=pos[sx][sy];    c[1][1]=a[1];    c[1][2]=a[2];    c[1][3]=a[3];    memset(b,true,sizeof(b));    for(int i=1;i<=n;i++)        for(int j=1;j<=m;j++)            if(A[i][j]==1) b[i][j]=false;    for(int k=1,l=1;l<=k;l++)    {        int ren=c[l][0],xiang1=c[l][1],xiang2=c[l][2],xiang3=c[l][3],xren=R[ren][1],yren=R[ren][2],x1=R[xiang1][1],y1=R[xiang1][2];        int x2=R[xiang2][1],y2=R[xiang2][2],x3=R[xiang3][1],y3=R[xiang3][2];        b[x1][y1]=false;        b[x2][y2]=false;        b[x3][y3]=false;        int ssx=f[ren][xiang1][xiang2][xiang3];        if(xiang1==v[1]&&xiang2==v[2]&&xiang3==v[3])        {            cout<<f[ren][xiang1][xiang2][xiang3];            return 0;        }        for(int i=1;i<=4;i++)            if(1<=xren+D[i][1]&&xren+D[i][1]<=n&&1<=yren+D[i][2]&&yren+D[i][2]<=m)            {                if(b[xren+D[i][1]][yren+D[i][2]]&&f[pos[xren+D[i][1]][yren+D[i][2]]][xiang1][xiang2][xiang3]==-1)                {                    f[pos[xren+D[i][1]][yren+D[i][2]]][xiang1][xiang2][xiang3]=ssx+1;                    c[++k][0]=pos[xren+D[i][1]][yren+D[i][2]];                    c[k][1]=xiang1;                    c[k][2]=xiang2;                    c[k][3]=xiang3;                }                int q[4];                q[1]=xiang1;                q[2]=xiang2;                q[3]=xiang3;                if(pos[xren+D[i][1]][yren+D[i][2]]==q[2])swap(q[1],q[2]);                if(pos[xren+D[i][1]][yren+D[i][2]]==q[3])swap(q[1],q[3]);                if(pos[xren+D[i][1]][yren+D[i][2]]==q[1])                    if(1<=xren+2*D[i][1]&&xren+2*D[i][1]<=n&&1<=yren+2*D[i][2]&&yren+2*D[i][2]<=m&&b[xren+2*D[i][1]][yren+2*D[i][2]])                    {                        q[1]=pos[xren+2*D[i][1]][yren+2*D[i][2]];                        for(int j=1;j<=3;j++)                            for(int k=j+1;k<=3;k++)                                if(q[k]!=0&&q[k]<q[j]) swap(q[j],q[k]);                                if(f[pos[xren+D[i][1]][yren+D[i][2]]][q[1]][q[2]][q[3]]==-1)                                {                                    f[pos[xren+D[i][1]][yren+D[i][2]]][q[1]][q[2]][q[3]]=ssx+1;                                    c[++k][0]=pos[xren+D[i][1]][yren+D[i][2]];                                    c[k][1]=q[1];                                    c[k][2]=q[2];                                    c[k][3]=q[3];                                }                    }            }        b[x1][y1]=true;        b[x2][y2]=true;        b[x3][x3]=true;    }    cout<<"17";    return 0;}

Tree

题目背景
SOURCE:NOIP2015-SHY-6

题目描述
给你一个有 N 个点 M 条边的无向带权连通图,每条边是白色或黑色,求一颗最小权的恰好有 K 条白边的生成树。

输入格式
第一行三个数 N、M、K ,分别表示点数、边数和所需的白边数。
接下来 M 行,每行四个数 u、v、w、c ,分别表示一条边的两个端点(从0开始标号)、边权和颜色(0表示白色,1表示黑色)。
输入数据保证有解。

输出格式
一行一个数表示所求生成树的边权和。

样例数据 1
输入  [复制]

2 2 1
0 1 1 1
0 1 2 0
输出

2
备注
【数据范围】
对 20% 的输入数据 :1≤N≤15。
对 100% 的输入数据 :1≤N≤50000,1≤M≤100000, 1≤w≤100。

貌似听大神讲过的一道原题。。。然而下来好像没懂就没管了。
论学一样懂一样的重要性。
二分大法好orz。

STD.CPP

#include<iostream>#include<cstdio>#include<cstdlib>#include<ctime>#include<cmath>#include<cstring>#include<string>#include<algorithm>using namespace std;int n,m,k,a,b,d,e,l,r=0,mid,cnt,cntt;int fa[50005];long long sum;struct node {    int u,v,w,c;}edge[100005],bian[100005];bool cmp(node a,node b){    if(a.w==b.w) return a.c<b.c;    return a.w<b.w;}int getfa(int x){    if(fa[x]==x) return x;    fa[x]=getfa(fa[x]);    return fa[x];}inline bool check(int mid) {    for(int i=1;i<=m;++i)    {        bian[i]=edge[i];        if(!edge[i].c) bian[i].w = edge[i].w + mid;    }    cnt=0,cntt=0,sum=0;    for(int i=0;i<n;++i) fa[i]=i;    sort(bian+1,bian+m+1,cmp);    for(int i=1;i<=m;++i)    {        a=getfa(bian[i].u);        b=getfa(bian[i].v);        if(a!=b) {            fa[a] = b;            cnt++;            if(!bian[i].c) {                cntt++;                sum += bian[i].w;            }            else sum+=bian[i].w;            if(cnt==n-1) break;        }    }    return (cntt >= k);}int main(){       cin>>n>>m>>k;    for(int i=1;i<=m;++i)    {        cin>>a>>b>>d>>e;        edge[i].u=a;        edge[i].v=b;        edge[i].w=d;        edge[i].c=e;    }    r=150;  l=-r;    while (l+1<r)    {        mid=(l+r)/2;        if (check(mid)) l=mid;        else r=mid;    }    check(l);cout << sum-k*l;    return 0;}

感想

T1现在能(变)苟(简)住(单)了。。T2,T3的暴力还要学学,打得好暴力,定能出奇迹
还有要补补二分和Dp了。。