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,是一個時代的終結。

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