2017.4.22考試

嗯,是陽光明媚的一天。
這麼好的天氣怎麼能不考點試呢?(正經臉▼-▼)
咳,不多說了,把題扔出來。

1.裝果子
果園裏有 n 顆果樹,每棵果樹都有一個編號 i(1≤i≤n)。小明已經把每棵果樹上的果子都摘下來堆在了這棵樹的下方,每棵樹下方的果子體積爲 ai。現在小明將拿來 m 個袋子把這些果子都裝進袋子裏。每個袋子的體積爲 v 。
小明會按照如下規則把果子裝進袋子裏:
(a) 從第 1 棵果樹開始裝起,由 1 到 n 一直裝到第 n 棵果樹。
(b) 如果這棵果樹下的果子能全部裝進當前這個袋子,就裝進去;如果不能,就關上當前這個袋子,打開一個新的袋子開始裝。
小明希望在能把所有果子都裝進袋子裏的前提下,v 儘量小。m 個袋子並不一定都要裝進果子。

輸入格式
輸入第 1 行,包含兩個整數 n 和 m 。
輸入第 2 行,包含 n 個整數 ai 。

輸出格式
輸出僅一行,表示最小的 v 。

輸入 
3 3
1 2 3
輸出
3

【樣例解釋】
每個袋子的體積爲 3 即可。前 2 棵果樹的果子裝在第一個袋子裏,第 3 棵果樹的果子裝在第二個袋子裏。第三個袋子不用裝了。

掃一眼,嗯,還算是比較簡單的二分。
但是數據……
對於100%的數據,n≤100,000,ai≤1,000,000,000。
於是忘了開long long的後果是毀滅性的。。。QAQ
那,放一隻代碼。

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

long long a[200010],n,m,t=0;

inline bool zql(long long x)
{
    if(x<t)
        return 0;
    long long cnt=1,ans=0;
    for(long long i=1;i<=n;i++)   //對比。
        if((ans+=a[i])>x)
            ans=a[i],cnt++;
    return cnt<=m;
}

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

    cin>>n>>m;
    long long l=0,r=1ll*(1e14),mid;   //給r了一個很大的值做上界。 
    for(long long  i=1;i<=n;i++)
    {
        cin>>a[i];
        t=max(t,a[i]);
    }
    while(l<=r)
    {
        if(zql(mid=l+r>>1))    //其實不打mid=(l+r)的括號也可以。+的優先級高於>>。
            r=mid-1;
        else 
            l=mid+1;
    }
    cout<<l<<endl;
    return 0;
}

(這麼簡單卻死在數據上……淚目)

很好,第二題。
2.零件加工
工匠小 K 最近有 n 個零件需要加工。每個零件都需要 ti 天的時間來完成,每個零件每延遲一天加工都要繳納一定的罰金 si 。延遲的天數爲從今天算起到該工作開始的那天,第一個零件加工沒有罰金。現在小 K 想知道怎樣安排加工順序可以使他要交的罰金最少,最少是多少?
這個數可能會很大,請輸出這個數對 m 取模後的結果。

輸入格式
輸入文件第一行爲一個整數 n ,表示需要加工的零件總數。
第二行爲一個整數 m ,表示答案要對 m 取模。
第 3~n+2 行,每行兩個整數 ti 和 si。

輸出格式
輸出僅一行,一個整數,表示小 K 最少要繳納的罰金對 m 取模的結果。

樣例數據
輸入 
2
100
2 33
33 2
輸出
4

【樣例解釋 】
先加工第一個,需要 2 天時間,再加工第二個。需要繳納的罰金爲 2×2=4 。

當時看完題第一個想到的就是貪心。
但是……當敲到排序的時候真是整個人都不好了。。。
嗯,其實可以把t與s的商的排序改換成積的排序。

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

還有,算後面的高精度相乘時,可以巧妙且完美的避開高精度乘法。

inline long long zql(long long a,long long b)
{
    long long r;
    for(r=0;b;a=(a<<1)%m,b>>=1)   //二進制計算所交罰金。 
        if(b&1)
            r=(r+a)%m;
    return r;
}

咳咳,如果崩潰的話,不妨看一個差不多但簡約版的。

inline long long zql(long long a,long long b)
{
    long long ret=0;
    while(a)
    {
        if(a&1)       //如果a是奇數
            ret=(ret+b)%m;  //就把ret加一個b。
        a>>=1;         //a除以2
        b=(b<<1)%m;    //b再乘2(其實積不變,不過把乘法慢慢轉換成加法)
    }
    return ret;
}

喏,完整的原版

#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
{
    long long t;
    long long s;
}a[100003];
long long n,ans=0,tt=0;
long long m;

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

inline long long zql(long long a,long long b)
{
    long long ret=0;
    while(a)
    {
        if(a&1)
            ret=(ret+b)%m;
        a>>=1;
        b=(b<<1)%m;
    }
    return ret;
}

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

    cin>>n;
    cin>>m;
    for(long long i=1;i<=n;i++)
        cin>>a[i].t>>a[i].s;
    sort(a+1,a+n+1,comp);
    for(long long i=2;i<=n;i++)
    {
        tt=(tt+a[i-1].t)%m;
        ans=(ans+zql(tt,(a[i].s%m)))%m;
    }
    cout<<ans<<endl;
    return 0;
}

嗯嗯,那麼第三題。
3.種樹
爲了綠化鄉村,H 村積極響應號召,開始種樹了。
H 村裏有 n 幢房屋,這些屋子的排列順序很有特點,在一條直線上。於是方便起見,我們給它們標上 1~n 。樹就種在房子前面的空地上。
同時,村民們向村長提出了 m 個意見,每個意見都是按如下格式:希望第 li 個房子到第 ri 個房子的房前至少有 ci 棵樹。
因爲每個房屋前的空地面積有限,所以每個房屋前最多隻能種 ki 棵樹。
村長希望在滿足村民全部要求的同時,種最少的樹以節約資金。請你幫助村長。

輸入格式
輸入文件輸入第 1 行,包含兩個整數 n,m 。
第 2 行,有 n 個整數 ki。
第 2~m+1 行,每行三個整數 li,ri,ci 。

輸出格式
輸出 1 個整數表示在滿足村民全部要求的情況下最少要種的樹。村民提的要求是可以全部滿足的。

樣例數據
輸入 
5 3
1 1 1 1 1
1 3 2
2 4 2
4 5 1
輸出
3

【樣例1解釋】
如圖是滿足樣例的其中一種方案,最少要種 3 棵樹。
(假裝這裏有圖的樣子,好吧,是圖複製不過來)

這麼裸的差分約束系統實在是太少見了。。。。
於是收穫了一個圓滿的Accepted。
直接上代碼。

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

const int N=500010;
struct node
{
    int to;
    int next;
    int w;
}e[N*4];
int n,m,tot;
int head[N],a[N],dis[N];
bool flag[N];

inline int init()
{
    int p=0;
    char c=getchar();
    if(c>'9'||c<'0')
        c=getchar();
    while(c<='9'&&c>='0')
    {
        p=p*10+c-'0';
        c=getchar();
    }
    return p;
}

inline void czh(int u,int v,int w)
{
    tot++;
    e[tot].to=v;
    e[tot].w=w;
    e[tot].next=head[u];
    head[u]=tot;
}

inline void zql()
{
    queue<int>q;
    memset(dis,-110,sizeof(dis));
    dis[0]=0;
    flag[0]=1;
    q.push(0);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        flag[u]=0;
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(dis[v]<dis[u]+e[i].w)
            {
                dis[v]=dis[u]+e[i].w;
                if(!flag[v])
                {
                    q.push(v);
                    flag[v]=1;
                }
            }   
        }
    }   
}

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

    int x,y,z;
    n=init();
    m=init();
    for(int i=1;i<=n;i++)
        a[i]=init();
    for(int i=1;i<=m;i++)
    {
        x=init();
        y=init();
        z=init();
        czh(x-1,y,z);
    }
    for(int i=1;i<=n;i++)
    {
        czh(i-1,i,0);
        czh(i,i-1,-a[i]);
    }
    zql();
    cout<<dis[n]<<endl;
    return 0;
}

PS. 關於建邊的一些碎碎念
很容易得出,滿足題目需要滿足下面三個方程:
dis[r]-dis[l-1]>=c,
s[i]>=s[i-1],
s[i]-s[i-1]<=k.
則在r和l-1之間連一條邊權爲-c的邊。
在i和i-1之間連一條邊權爲0的邊。
在i-1和i之間連一條邊權爲k的邊。
(爲什麼一定要這樣連?記住要滿足最短路徑的方程。)

吶,陽光依舊挺好的,又是一週。
夏日靜好^-^

——我認爲return 0,是一個時代的終結。

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