tEST 1 for NOIP 2017.9.9.

头更大

这个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了。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值