2015-9-13 NOIP模擬賽解題報告(by hzwer)


小奇挖礦

「題目背景」

小奇要開采一些礦物,它駕駛着一台帶有鑽頭(初始能力值w)的飛船,按既定路線依次飛過喵星系的n個星球。

「問題描述」

星球分為2類:資源型和維修型。

1.資源型:含礦物質量a[i],若選擇開采,則得到a[i]p的金錢,之后鑽頭損耗k%,即p=p(1-0.01k)

2.維修型:維護費用b[i],若選擇維修,則支付b[i]p的金錢,之后鑽頭修復c%,即p=p(1+0.01c)

(p為鑽頭當前能力值)

注:維修后鑽頭的能力值可以超過初始值

請你幫它決策最大化這個收入

「輸入格式」

第一行4個整數n,k,c,w。

以下n行,每行2個整數type,x。

type為1則代表其為資源型星球,x為其礦物質含量a[i];

type為2則代表其為維修型星球,x為其維護費用b[i];

「輸出格式」

輸出一行一個實數(保留兩位小數),表示要求的結果。

「樣例輸入」

5 50 50 10

1 10

1 20

2 10

2 20

1 30

「樣例輸出」

375.00

「數據范圍」

對於30%的數據 n<=100

對於50%的數據 n<=1000,k=100

對於100%的數據 n<=100000,0<=k,c,w,a[i],b[i]<=100

保證答案不超過10^9

動態規划。因為觀察到每一個操作對它后面的操作具有影響,根據DP無后效性原則,我們考慮從后往前DP。然后觀察到每次操作后面的收益都和p成正比。所以我們設\(dp[i]\)為在第\(i\)個星球且當前\(p=1\)(單位1)的時候最大的收益。

代碼如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXN 100010
using namespace std;
double dp[MAXN];
int k,c,n,w;
int name[MAXN],val[MAXN];   
int main()
{
    scanf("%d%d%d%d",&n,&k,&c,&w);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&name[i],&val[i]);
    double kk=(1-0.01*(double)k);
    double cc=(1+0.01*(double)c);
    for(int i=n;i>=1;i--)
    {
        if(name[i]==1) dp[i]=max(dp[i+1],dp[i+1]*kk+val[i]);
        else dp[i]=max(dp[i+1],dp[i+1]*cc-val[i]);
    }
    printf("%.2lf\n",dp[1]*w);
    return 0;
}

小奇的數列

「題目背景」

小奇總是在數學課上思考奇怪的問題。

「問題描述」

給定一個長度為n的數列,以及m次詢問,每次給出三個數l,r和P,詢問 (a[l’] + a[l’+1] + … + a[r’]) mod P的最小值。

其中l <= l’ <= r’ <= r。

即模意義下的區間子串和最小值。

「輸入格式」

第一行包含兩個正整數n和m,表示數列的長度和詢問的個數。
第二行為n個整數,為a[1]..a[n]。
接下來m行,每行三個數l,r和P,代表一次詢問。

「輸出格式」

對於每次詢問,輸出一行一個整數表示要求的結果

「樣例輸入」

4 2

8 15 9 9

1 3 10

1 4 17

「樣例輸出」

2

1

「數據范圍」

對於20%的數據 n<=100,m<=100,p<=200

對於40%的數據 n<=200,m<=1000,p<=500

對於70%的數據 n<=100000,m<=10000,p<=200

對於100%的數據 n<=500000,m<=10000,p<=500,1<=a[i]<=10^9

正解是平衡樹。。。但是我不太會。。。這個坑先放這里,,,(然后附上黃學長博文鏈接:http://hzwer.com/7613.html


小奇回地球

「題目背景」

開學了,小奇在回地球的路上,遇到了一個棘手的問題。

「問題描述」

簡單來說,它要從標號為1的星球到標號為n的星球,某一些星球之間有航線。由於超時空隧道的存在,從一個星球到另一個星球時間可能會倒流,而且,從星球a到b耗費的時間和星球b到a耗費的時間不一定相同。

宇宙法規定:“禁止在出發時間前到達目的地。”

每艘飛船上都有速度調節裝置,可以調節飛行的時間。其功能可以使得整次航程中所有兩星球間的飛行時間增加或減少相同的整數值。你的任務是幫助它調整速度調節器,找出一條最短時間到達目的地的路徑。

「輸入格式」

輸入文件包含多組數據,第1個數為T,表示數據組數。

對於每組數據,輸入第1行為兩個正整數n,m,為星球的個數和星球間的路線數。接下來m行,每行三個整數i,j和t,表示由星球i到星球j飛行的時間為t。由i到j最多只會有一條飛行線路。

「輸出格式」

輸出文件共T行,每組數據輸出一行。

如果可以通過調節速度調節器完成任務,則輸出一個非負整數,表示由星球1到星球n的最短時間。(注意最短時間要大於或者等於0)。

如果不能由星球1到達星球n,則輸出-1。

「樣例輸入」

1

4 5

1 2 1

1 3 1

2 3 -3

3 1 1

3 4 1

「樣例輸出」

2

「樣例解釋」

把速度控制器的值設為1,相當於每個時間值加1,得到的最短路徑為1→2→3→4,所需時間為2+(-2)+2=2。

「數據范圍」

1,2號測試點,保證所有星球出度不超過1

3,4號測試點,n<=10

5,6號測試點,-100<=t<=100

對於100%的數據T<=10,n<=100,m<=n*(n-1),-100000<=t<=100000

數據隨機和構造結合生成

就是枚舉出來答案然后判斷合不合法。枚舉的過程可以使用二分加速。

注意中間會有負環的情況。但是因為不是每個點都會到終點的。所以如果到終點的最短路上沒有負環而且從起點到終點的最短路的值大於0即可。

開始先建個反圖,使用dfs找一下哪些節點是可以到終點的,打個標記。

我寫題的時候zz了,每次更換枚舉的增加值的時候還重新建邊了。。。。但是其實是可以在spfa跑最短路的時候直接處理的。。這個以后要記住qwq

然后就沒有什么了。。。。

代碼如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define MAXN 100100
using namespace std;
int n,m,edge_number,t,ans;
int u[MAXN],v[MAXN],w[MAXN],len[MAXN];
int dis[MAXN],done[MAXN],head[MAXN],avl[MAXN];
struct Edge{int nxt,to,dis;}edge[MAXN<<1];
inline void add(int from,int to,int dis)
{
    edge[++edge_number].dis=dis;
    edge[edge_number].to=to;
    edge[edge_number].nxt=head[from];
    head[from]=edge_number;
}
inline int spfa(int x)
{
    queue<int>q;
    for(int i=1;i<=n;i++) dis[i]=2147483647,done[i]=0,len[i]=0;
    q.push(1); done[1]=1; dis[1]=0; len[1]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        done[u]=0;
        for(int i=head[u];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(avl[v]==0) continue;
            if(dis[v]>dis[u]+edge[i].dis+x)
            {
                dis[v]=dis[u]+edge[i].dis+x;
                if(!done[v])
                {
                    done[v]=1;
                    len[v]=len[u]+1;
                    if(len[v]>n) 
                        return -1;
                    q.push(v);
                }   
            }
        }
    }
    return dis[n];
}

void dfs(int u)
{
    avl[u]=1;
    for(int i=head[u];i;i=edge[i].nxt)
        if(!avl[edge[i].to])
            dfs(edge[i].to);
}
int main()
{
    scanf("%d",&t);
    while(t--) 
    {
        memset(head,0,sizeof(head));
        memset(avl,0,sizeof(avl));
        scanf("%d%d",&n,&m);
        int l=-100000,r=100000;
        for(int i=1;i<=m;i++)
            scanf("%d%d%d",&u[i],&v[i],&w[i]);
        for(int i=1;i<=m;i++)
            add(v[i],u[i],w[i]);
        dfs(n);
        memset(head,0,sizeof(head));
        edge_number=0;
        for(int i=1;i<=m;i++)
            add(u[i],v[i],w[i]);
        if(!avl[1])
        {
            printf("-1\n");
            continue;
        }
        while(l<r)
        {
            int mid=(l+r)>>1;
            long long cur=spfa(mid);
            if(cur>=0) r=mid,ans=cur;
            else l=mid+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

100+40+50=190分收場。。。。真真是蒟蒻被吊打的節奏啊qwq。。。。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com