2017.10.18 隊內胡策 D5

昨天考的題,今天早晨才把T2調完,竟然卡了精度

原題鏈接:
T1:gjh自己出的(靈感來源:codevs 1742 爬樓梯 鏈接
T2:luogu 2656 採蘑菇 鏈接
T3:codevs 1456 隱藏口令 鏈接
T4:luogu 1984 燒水問題 鏈接

T1
這裏寫圖片描述

第一問DP,第二問隨便打打貪心
結果我第二問貪心在模擬的過程中就打次了(第一次貪心模擬輸給了DP)

不多說 很簡單
代碼:

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

const int maxn=100000+10,mo=19260817;
int n,m;
int h[maxn],f[maxn],ans,cnt;

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i) 
    {
        int x,y;
        scanf("%d%d",&x,&y);
        h[x]=y;
    }
    for(int i=1;i<=n;++i)
      if(!h[i]) h[i]=1;
    f[1]=f[0]=1;
    cnt=h[1];
    ans=1;
    for(int i=2;i<=n;++i)
    {
        if(cnt+h[i]<=4) cnt+=h[i];
        else 
        {
            cnt=h[i];
            ans++;
        }
        int last=h[i];
        int j;
        for(j=i-1;j>=0;--j)
        {
            last+=h[j];
            f[i]=(f[i]%mo+f[j]%mo)%mo;
            if(last>4) break;
        }
    }
    printf("%d %d",f[n],ans);
    return 0;
}

T2:(模板題)
這裏寫圖片描述

思路:tarjan縮點,處理每一個scc裏最大的蘑菇數量,跑最長路

現在說一下我哪裏被卡了….

在統計每一個scc裏面的蘑菇數量時,對於每一條邊,都要反覆採集直到數量變爲0

一開始,我的程序是這樣寫的:

int cal(int i)
{
    int f=e[i].f,t=e[i].t;
    int v=e[i].v;
    int ret=v;
    double k=e[i].k;
    while(v)
    {
        ret+=v*k;
        v*=k;
    }
    return ret;
}

死活wa了4個點,大寫的懵逼

後來我發現這樣寫可以過:

int cal(int i)
{
    int f=e[i].f,t=e[i].t;
    int v=e[i].v;
    int ret=v;
    double k=e[i].k;
    while(v)
    {
        v*=k;
        ret+=v;
    }
    return ret;
}

然後就更懵逼了
(內心:這倆程序有什麼不同??!!)
我把兩個程序單獨摘出來對拍。結果真拍出了一組不同的結果:
輸入:6840 0.7
輸出:第一種:21555 第二種:21554

我把兩個程序的計算流程輸出,發現有一個地方出現了問題:
中間有一個過程,需要答案+(20*0.7)
第一個程序,ans加了14,第二個,ans加了13
兩段程序的區別就是:
前者直接把ans加上v*k,後者是先把v*k賦值給一個int型變量,再把ans加上這個int變量。

後來知道20*0.7=14.0,double中下取整會變成13.0,但是始終不知道這兩種加法到底有什麼區別,按理說計算結果都應該是ans+=13

不過至少知道了14.0這種東西是以13.99999..存儲的,想把它下取整變成14的話,就加0.00005之類的

因爲這個調了很久

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<vector>
#include<queue>
using namespace std;

const int maxn1=80000+500,maxn2=200000+500;
int n,m,cnt,s,scc_cnt,Index,ans;
int fist[maxn1],nxt[maxn2],low[maxn1],dfn[maxn1],where[maxn1],num[maxn1];
int fi[maxn2],ti[maxn2],vi[maxn2],dis[maxn1];
double ki[maxn2];
vector<int>which[maxn1];
bool instack[maxn1],vis[maxn1];
struct hh
{
    int f,t,v;
    double k;
}e[maxn2];
queue<int>q;
stack<int>S;

void init()
{
    memset(fist,-1,sizeof(fist));
    memset(nxt,0,sizeof(nxt));
    memset(dfn,-1,sizeof(dfn));
    memset(vis,0,sizeof(vis));
    cnt=0;
}
void build(int f,int t,int v,double k)
{
    e[++cnt]=(hh){f,t,v,k};
    nxt[cnt]=fist[f];
    fist[f]=cnt;
}
int cal(int i)
{
    int f=e[i].f,t=e[i].t;
    int v=e[i].v;
    int ret=v;
    double k=e[i].k;
    while(v)
    {
        v*=k;
        ret+=v;
    }
    return ret;
}
void tarjan(int i)
{
    dfn[i]=low[i]=++Index;
    S.push(i);
    for(int x=fist[i];x!=-1;x=nxt[x])
    {
        int j=e[x].t;
        if(dfn[j]==-1)
        {
            tarjan(j);
            low[i]=min(low[i],low[j]);
        }
        else if(!where[j]) low[i]=min(low[i],dfn[j]);
    }
    if(dfn[i]==low[i])
    {
        scc_cnt++;
        int j;
        do
        {
            j=S.top();
            S.pop();
            which[scc_cnt].push_back(j);
            where[j]=scc_cnt;
        }
        while(j!=i);
    }
}
void done()
{
    for(int u=1;u<=n;++u)
    {
        for(int i=fist[u];i!=-1;i=nxt[i])
        {
            int v=e[i].t;
            if(where[v]==where[u])
              num[where[v]]+=cal(i);
        }
    }
    init();
    for(int i=1;i<=m;++i)
    {
        int f=fi[i],t=ti[i];
        if(where[f]!=where[t])
          build(where[f],where[t],vi[i],ki[i]);
    }
}
void spfa(int ds)
{
    vis[ds]=true;
    q.push(ds);
    dis[ds]=num[ds];
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=false;
        for(int i=fist[u];i!=-1;i=nxt[i])
        {
            int v=e[i].t;
            if(dis[v]<dis[u]+e[i].v+num[v])
            {
                dis[v]=dis[u]+e[i].v+num[v];
                //e[i].v*=e[i].k;
                if(!vis[v])
                {
                    vis[v]=true;
                    q.push(v);
                }
            }
        }
    }
}
int main()
{
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&fi[i],&ti[i],&vi[i]);
        cin>>ki[i];
        build(fi[i],ti[i],vi[i],ki[i]);
    }
    scanf("%d",&s);
    for(int i=1;i<=n;++i)
      if(!where[i]) tarjan(i);
    done();
    spfa(where[s]);
    for(int i=1;i<=scc_cnt;++i) ans=max(ans,dis[i]);
    printf("%d",ans);
    return 0;
}

T3:
這裏寫圖片描述
考試的時候沒想到
其實直接兩兩比較就可以,兩個指針,i指當前找到的字典序最小的串的開頭字符,j指將要和它進行比較的字符串的開頭字符,每次指針往後移,再用一個k每次比較兩個指針往後k位的字符就可以了
一開始想的是:(僞代碼)

if(s[i+k]>s[j+k]) 說明當前比較的字符串比之前的最小字符串更優,所以  i=j,j++;
if(s[i+k]<s[j+k]) 說明當前字符串不如之前的最小字符串更優,所以  j++  比較下一位

但是會TLE,而且TLE了65%
考慮優化
首先,如果每次指針j都往後只跳一個格子,肯定有冗雜的操作
對於s[i+k]>s[j+k]的情況,我們可以直接把j跳到i+k+1的地方,也就是說,對於以[i~i+k+1)這段區間,一定不會是答案。(如果j > i+k+1,直接跳到j,因爲我們要保證i始終在j的左邊,便於統計答案)
這裏不是很好想。
來一波證明
即證明:對於以[i~i+k+1)這段區間開頭的字符串,一定存在某字符串的字典序比它小
設該區間中某一個字符i’,則對於字符串s[i’~i+k],長度len爲i+k-i’+1,則在區間[j~j+k+1),長度相同的以j’開頭的字符串s[j’~j+k],由於這兩段區間在比對到i+k和j+k之前比對時,已經確定,s[i’~i+k-1]一定等於s[j’~j+k-1],且s[i+k]>s[j+k],那麼s[i’~i+k]的字典序一定大於s[j’~j+k],所以s[i’~i+k]一定不是答案。

對於s[i+k]

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

int n,ans;
string ss,s;

int main()
{
    scanf("%d",&n);
    while(cin>>ss) s+=ss;
    s+=s;
    int i=0,j=1;
        while(i<n&&j<n)
    {
        bool flg=0; 
        for(int k=0;k<n;++k)
        {
            if(s[i+k]>s[j+k]) 
            {
                int x=i;
                i=j;
                j=max(j,x+k)+1;
                break;
            }
            else if(s[i+k]<s[j+k])
            {
                j=j+k+1;
                break;
            }
            if(k==n-1)
            {
                flg=1;
                break;
            }
        }
        if(flg) break;
    }
    printf("%d",i);
    return 0;
}

T4:
這裏寫圖片描述
這裏寫圖片描述
數學推理題,代碼賊短,懶得寫了,直接貼題解吧╮(╯▽╰)╭

最大的情況就是使已經燒開的水的熱量被儘可能的利用。 我們發現,當一杯水的熱量一個個的往右傳遞下去的話,熱量最不容易浪費。

熱量的傳遞 實際數據解釋: 假設有5杯水: 0 0 0 0 0

第一杯水: 100 0 0 0 0 –> 6.25 50 25 12.5 6.25 第二杯水: 6.25 100 25 12.5
6.25–> 6.25 21.875 62.5 37.5 21.875 第三杯水: 6.25 21.875 100 37.5 21.875–>6.25 21.875 45.3125 68.75 45.3125 第四杯水: 6.25 21.875 45.3125 100 45.3125–> 6.25 32.875 45.3125 72.65625 72.65625 第五杯水:…… 100 。

我們發現 這五杯水被燒開前只進行熱傳遞可以達到的溫度爲 0 50 62.5 68.75 72.65625 還需要升高的溫度爲: 100
50 37.5 31.25 27.34375 發現: 50/100=1/2 、37.5/50=3/4
、31.25/37.5=5/6、27.34375/31.25=7/8 規律:第i杯水需要上升的溫度爲第i-1杯水需要上升的溫度*
(2*(i-1)-1)/(2*(i-1)).
**熱量的傳遞 公式解釋(摘自洛谷題解) :

推導:設沸騰溫度爲a //則第一杯溫度爲a,需要加熱t1=a //第二杯可以中和的最高溫度爲a/2,需要加熱t2=a/2
//第三杯可以中和的最高溫度爲t3=(a/4+a)/2=5a/8,需要加熱t3=3a/8
//第四杯可以中和的最高溫度爲t4=((a/8+5a/8)/2+a)/2=11a/16,需要加熱t4=5/16
//則t3/t2=3/4=1-1/4, t4/t3=5/6=1-1/6 //繼續推導得t(n+1)/t(n)=1-1/2n;

最後遞推求解。

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

int n;
double a=4200,k,ans=0,x=100;

int main()
{
    freopen("water.in","r",stdin);
    freopen("water.out","w",stdout);
    scanf("%d",&n);
    k=(double) 1/n;
    ans+=a*x*k;
    for(int i=2;i<=n;++i)
    {
        x*=(double)(2*(i-1)-1)/(2*(i-1));
        ans+=a*x*k;
    }
    printf("%.2lf",ans);
    return 0;
} 
發佈了77 篇原創文章 · 獲贊 2 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章