2017.4.15考试

又是一次考试。
一如既往的垫底,收获10分。(共三道题)

首先是第一题:
1、减法(calc.c/cpp/pas)
【题目大意】
给出a 和 b,输出 a^b - b^a
【输入数据】
第一行两个数 a和 b,空格隔开
【输出数据】
一行一个数,表示 a ^ b – b ^ a
【输入样列】
2 3
【输出样列】
-1
【数据规模】
1<=a,b<=100

如题。(:
很明白的一道题,一眼看过去,公式都给好了。
然后看了一眼数据范围,1<=a,b<=100
很好,高精度……

#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn=2600;
struct BigNumber
{
    int n,a[maxn];
    BigNumber(int x=1){a[n=1]=x;}
    void operator*=(int x)
    {
        for(int i=1;i<=n;++i)a[i]*=x;
        for(int i=1;i<=n;++i)if(a[i]>=10000)
        {
            a[i+1]+=a[i]/10000;
            a[i]%=10000;
        }
        while(a[n+1]>0)
        {
            ++n;
            if(a[n]>=10000)
            {
                a[n+1]+=a[n]/10000;
                a[n]%=10000;
            }
        }
    }//即定义结构体并压位,e.g.(1)(0)(0)(0)(0)(0)→(00001)(00000) [每个括号为数组中的一位]
    void print()
    {
        printf("%d",a[n]);
        for(int i=n-1;i;--i)printf("%04d",a[i]);puts("");
    }
};

BigNumber operator-(BigNumber a,const BigNumber &b)
{
    BigNumber c(0);
    for(int i=1;i<=a.n;++i)
    {
        c.a[i]=a.a[i]-b.a[i];
        if(c.a[i]<0)--a.a[i+1],c.a[i]+=10000;
    }
    c.n=1;
    for(int i=a.n;i;--i)if(c.a[i]>0){c.n=i;break;}
    return c;
}

bool operator<=(const BigNumber &a,const BigNumber &b)
{
    if(a.n>b.n)return 0;
    if(a.n<b.n)return 1;
    for(int i=a.n;i;--i)if(a.a[i]<b.a[i])return 1;else if(a.a[i]>b.a[i])return 0;
    return 1;
}//比较大小时用总和比较。

int main()
{
    //freopen("calc.in","r",stdin);
    //freopen("calc.out","w",stdout);

    int a,b;
    scanf("%d%d",&a,&b);

    static BigNumber M,N;
    for(int i=1;i<=b;++i)M*=a;
    for(int i=1;i<=a;++i)N*=b;

    if(N<=M)(M-N).print();
    else
    {
        printf("-");
        (N-M).print();
    }

    return 0;
}

(摘自标准题解)
很好很强大。
但是可以不用高精度,用它的思想。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;

int a,b;
int x[220],y[220];

int zql()
{
    for(int i=219;i>=0;i--)
    {
        if(x[i]==y[i])
            continue;
        return x[i]<y[i];
    }
    return false;
}

int main()
{
    cin>>a>>b;
    x[0]=y[0]=1;
    for(int i=1;i<=b;i++)
    {
        int r=0;
        for(int j=0;j<220;j++)
        {
            x[j]=x[j]*a+r;
            r=x[j]/10;
            x[j]%=10;
        }
    }

    for(int i=1;i<=a;i++)
    {
        int r=0;
        for(int j=0;j<220;j++)
        {
            y[j]=y[j]*b+r;
            r=y[j]/10;
            y[j]%=10;
        }
    }

    if(zql())
    {
        for(int i=0;i<220;i++)
            swap(x[i],y[i]);
        cout<<"-";
    }

    int r=0;
    for(int i=0;i<220;i++)
    {
        x[i]=x[i]-y[i]+r+10;
        r=x[i]/10-1;
        x[i]%=10;
    }
    for(r=219;r;r--)
        if(x[r])
            break;
    for(int i=r;i>=0;i--)
        cout<<x[i];
    return 0;       
}

r是进位,x[i],y[i]分别存储a^b和b^a,然后一位一位的相减。
果真清爽多了。

那么第二题。
2、tree.pas/c/cpp
【题目大意】
给出一个树,每个结点 i都有一个权值W[i]。现在要求你选出一些点,满足任意两个点之间都不能够有边相连,并且使得选出的点的总权值最大。
【输入数据】
第一行一个数 N表示结点数目
接下来一行N个数分别表示结点 1~N 的权值。
接下来 N-1行每行两个数表示一条边
【输出数据】
一个数表示最大的总权值
【Sample Input】
2
10 8
1 2

【Sample Output】
10
【数据规模】
30%:N<=10
100%:N<=10,0000,|W[i]|<=10^9

据说是比较简单的树形DP。
嗯,所以基本思路应该是,在一棵树上,若想要选出的任意两点无连边,选完某点后,既不能选它的父亲,也不能选它的儿子。
所以先建边。

inline void zql(int u,int v)
{
    next[++tot]=first[u];
    first[u]=tot;
    en[tot]=v;
}

然后就可以开心的DFS了(认真脸)

inline void czh(int x)
{
    int k;
    v[x]=1;
    g[x]=0,f[x]=w[x];
    for(int i=first[x];i;i=next[i])
        if(!v[k=en[i]])
        {
            czh(k);
            g[x]+=max(f[k],g[k]);
            f[x]+=g[k];
        }
}

最后输出g[1]和f[1]的最大值即可。
总代码如下:

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;

typedef long long ll;
const ll ll_inf=1000000000000000011;
int n,tot;
int first[100003],next[200006],en[200006],w[100003];
ll g[100003],f[100003];
bool v[100003];

inline void zql(int u,int v)
{
    next[++tot]=first[u];
    first[u]=tot;
    en[tot]=v;
}

inline void czh(int x)
{
    int k;
    v[x]=1;
    g[x]=0,f[x]=w[x];
    for(int i=first[x];i;i=next[i])
        if(!v[k=en[i]])
        {
            czh(k);
            g[x]+=max(f[k],g[k]);
            f[x]+=g[k];
        }
}

int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);

    int u,v;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>w[i];
    for(int i=1;i<n;i++)
    {
        cin>>u>>v;
        zql(u,v),zql(v,u);
    }
    czh(1); 
    cout<<max(g[1],f[1])<<endl;
    return 0;
}

最后我们来看看神奇的第三题。
3、cheer.pas/c/cpp
【题目描述】
Farmer John 变得非常懒,他不想再继续维护供奶牛之间供通行的道路。
道路被用来连接 N(5≤N≤10,000)个牧场,牧场被连续地编号为 1..N ,每一个牧场都是一个奶牛的家,FJ 计划除去 M(N-1≤M≤100,000)条道路中尽可能多的道路,但是还要保持任意两个牧场之间是连通的。
你首先要决定哪些道路是需要保留的 N-1 条道路。第 j 条双向道路连接了牧场 S[j] 和 E[j] (1≤S[j],E[j]≤N;S[j]≠E[j]),而且走完它需要 L[j](0≤L[j]≤1,000)的时间。没有两个牧场是被一条以上的道路所连接。
奶牛们非常伤心,因为她们的交通系统被削减了,所以你需要到每一个奶牛的住处去安慰她们。每次你到达第 i 个牧场的时候(即使你已经到过),你必须花去 Ci的时间和奶牛交谈。
安慰奶牛的出发牧场由你自己确定,并且在安慰完所有奶牛以后你必须回到起始位置。假设 Farmer John 采纳了你的建议,请计算出使所有奶牛都被安慰的最少时间。
【输入格式】
第 1 行:用空格隔开的两个整数 N 和 M 。
第 2..N+1 行:第 i+1 行包含了一个整数:C[i]。
第 N+2..N+M+1 行:第 N+j+1 行包含用空格隔开的三个整数:S[j],E[j] 和 L[j]。
【输出格式】
输出 1 行,一个整数,即所需要的总时间(包含和在你所在的牧场的奶牛的两次谈话时间)。
【样例数据】
输入
5 7
10
10
20
6
30
1 2 5
2 3 5
2 4 12
3 4 17
2 5 15
3 5 6
4 5 12
【输出】
176
【样例说明】
从牧场 4 出发,然后按照 4,5,4,2,3,2,1,2,4 的顺序来访问奶牛们,总共需要 176 个单位的时间。

大家都说这是裸奔的最小生成树……
然而天真的我并没有想到。
由题,每经过一条边,所花费的时间是边的权值和两点的值之和。
排序的时候要用总和来排序,而不仅仅是边的权值。
记住选定牧场后初始化时需加上起点的值,即将ans初始化为c[i]中的最小值。
废话不多说,上代码:

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;

struct node
{
    int x;
    int y;
    int w;
}e[100005];
int n,m,ans;
int f[100005],c[100005];

inline bool comp(const node &a,const node &b)
{
    return a.w<b.w; 
}

inline int find(int x)
{
    int u,v;
    for(u=f[x];u!=f[u];u=f[u]);
    for(;v!=u;)
    {
        v=f[x];
        f[x]=u;
        x=v;
    }   
    return u;
}

int main()
{
    freopen("cheer.in","r",stdin);
    freopen("cheer.out","w",stdout);

    int ans=~0u>>1;        //尽量取一个很大的数。~为取反运算符,即将1变为0,0变为1,u指无符号数。其实本意就是取一个很大的数,便于后面与c[i]比较取最小值。 
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        f[i]=i;
        cin>>c[i];
        ans=min(ans,c[i]);
    }
    for(int i=1;i<=m;i++)
    {
        cin>>e[i].x>>e[i].y>>e[i].w;
        e[i].w=e[i].w*2+c[e[i].x]+c[e[i].y];
    }

    sort(e+1,e+1+m,comp);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        u=find(e[i].x),v=find(e[i].y);
        if(u!=v)
        {
            ans+=e[i].w;
            f[u]=v;
        }
    }

    cout<<ans<<endl;
    return 0;
}

呐,Kruskal其实挺短的。
综上。

——我认为return 0,是一个时代的终结。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章