2018.7.19模拟考试

我的运气真的有够差...代码没能提交到评测姬上去...虽然就算提交了也是爆零

T1 题意简述:合并数列相邻项(也可不合并)使得数列单调不减并使项数最多。求原项数与合并后项

             数的差。

             0<n<=5000,0<a[i]<=2147483647

   解题思路:首先考虑贪心,显然错误(如:5 1 7 7 8)。

             考虑dp。dp[i]表示数列前i个数合并后最多的项数,lst[i]表示以数列第i项结尾的需合

             并区间的数值和。

             考虑递推式,发现分两种情况。(枚举k)

             1.a[k]>=lst[i] dp[k]=dp[i]+1,lst[k]=a[k],k++;

             2.a[k]<lst[i] lst[i]+=a[k],k++。直至满足情况一。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
ll n,dp[5001],sum[5001],a[5001],lst[5001];
int main()
{
    freopen("tower.in","r",stdin);
    freopen("tower.out","w",stdout);
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++)
        scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
    dp[1]=1;lst[1]=a[1];
    for(ll i=2;i<=n;i++)
        for(ll j=i-1;j>=0;j--)
            if(sum[i]-sum[j]>=lst[j])
            {
                dp[i]=dp[j]+1;
                lst[i]=sum[i]-sum[j];
                break;
            }
    printf("%lld
",n-dp[n]);
    return 0;
}

T2 题意简述:在n天里共有m项工作和p节课程,每项工作可以无限做,每节课程只能上一次。对于每一

             项工作有2个参数,即所需时长和等级要求。只有达到了等级要求才能做这项工作。每项

             工作的报酬相等。对于每一节课程有3个参数,即开始时间,所需时长和上完课程后等级

             会变为何值。初始人物等级为1,求在n天内最多得到多少报酬。

             n≤10000m≤10000,p≤100

             fin[i]≤10000,chg[i]≤100,need[i]≤100,tim[i]≤10000

   解题思路:显然dp。dp[i][j]表示第i天,等级为j所能得到的最多报酬。

             考虑递推式,发现分3种情况。

             1.划水一天。(这不就是我吗)dp[i][j]=max(dp[i][j],dp[i-1][j]);

             2.工作一天。dp[i][j]=max(dp{i][j],dp[i-tim[k]][j]);(j>=need[k])

             3.从这一天起开始上课。dp[fin[k]][chg[k]]=max(dp[fin[k]][chg[k]],dp[i-1][j])。

             还有一些细节和优化。

             1.考虑工作,发现等级要求高且所需时长长的工作可以直接舍去。

             2.第3项递推式的j要从1枚举到100,取max后与上课后的收益比较。

             3.看见-1了吗。没错那里就是我倒下的地方。

               注意若从第i天开始上课,收益只能计算前i-1天的。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
int n,ans,cls,wrk,dp[10001][101],minlen[101];
struct uio{
    int sta,fin,len,chg;
}cla[101];
struct oiu{
    int len,lvl;
}wor[10001];
bool cmp(oiu x,oiu y) {return x.lvl<y.lvl;}
bool cmp1(uio x,uio y) {return x.sta<y.sta;}
int main()
{
    freopen("wrk.in","r",stdin);
    freopen("wrk.out","w",stdout);
    scanf("%d%d%d",&n,&cls,&wrk);
    for(int i=1;i<=cls;i++)
    {
        scanf("%d%d%d",&cla[i].sta,&cla[i].len,&cla[i].chg);
        cla[i].fin=cla[i].sta+cla[i].len;
    }
    for(int i=1;i<=wrk;i++)
        scanf("%d%d",&wor[i].lvl,&wor[i].len);
    sort(wor+1,wor+1+wrk,cmp);
    sort(cla+1,cla+1+cls,cmp1);
    for(int i=1;i<=100;i++)
    {
        int j=1,mn=INF;
        while(j<=wrk&&wor[j].lvl<=i)
            mn=min(mn,wor[j].len),j++;
        minlen[i]=mn;
    }
    memset(dp,0xc0,sizeof(dp));
    dp[0][1]=0;
    for(int i=1;i<=n;i++)
    {
        int mx=-INF,cnt=1;
        for(int j=1;j>=100;j++)
            mx=max(mx,dp[i-1][j]);
        while(i==cla[cnt].sta&&cnt<=wrk)
            dp[cla[cnt].fin][cla[cnt].chg]=max(dp[cla[cnt].fin][cla[cnt].chg],mx),cnt++;
        for(int j=1;j<=100;j++)
        {
            if(i-minlen[j]>=0)
                dp[i][j]=max(dp[i][j],dp[i-minlen[j]][j]+1);
            dp[i][j]=max(dp[i][j],dp[i-1][j]);
        }
    }
    for(int j=1;j<=100;j++)
        ans=max(ans,dp[n][j]);
    printf("%d
",ans);
    return 0;
}

T3 题意简述:对于一个有n个点,m条边且至多有一个点度数>2的图,任选k个点,使图上任意一点到这

             k个点中的任意一点的最大距离最小。求这个最大距离。

             1≤n≤2000,n-1<=m<=n*(n-1)/2

   解题思路:对于min(max(...,...))或max(min(...,...))的题目,考虑二分答案。

             对于度数全部小于等于2的图,其只有可能是一条链或一个环。

             若有且仅有一个点度数大于2,那么这个图将类似菊花图,这个点暂且称为花心。

             花心所拥有的性质为:花心为任意一条链或一个环的一部分。

             记录所有点的度数,若无花心则直接输出答案。ans=ceil((n-k)/(2*k))。

             若有花心,考虑哪个点与花心距离最小。由于n仅有2000,枚举即可。

             bfs求出所枚举点到花心的距离。若距离大于二分答案,直接跳过,否则继续。

             将与所枚举点距离小于二分答案的点都打上标记。剩余的点必会构成若干条链。

             dfs求出每条链的长度后按照情况一计算答案即可。ans=sigma(ans[i])+1

             注意一开始枚举的那个点也要算。最后取min看符不符合k即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#define INF 0x3f3f3f3f
using namespace std;
int n,m,k,cnt,len,root,d[2001],head[2001];
int dep[2001],vis[2001];
struct uio{
    int nxt,to;
}edge[200001];
int ceil_int(int x,int y) {return x/y+(x%y>0);}
void add(int x,int y)
{
    edge[++cnt].nxt=head[x];
    edge[cnt].to=y;
    head[x]=cnt;
}
void bfs(int x)
{
    memset(dep,-1,sizeof(dep));
    queue<int> que;
    while(!que.empty()) que.pop();
    que.push(x);
    dep[x]=0;
    while(!que.empty())
    {
        int now=que.front();
        que.pop();
        for(int i=head[now];i;i=edge[i].nxt)
        {
            int y=edge[i].to;
            if(dep[y]==-1)
                dep[y]=dep[now]+1,que.push(y);
        }
    }
}
int dfs(int x)
{
    vis[x]=1,len++;
    for(int i=head[x];i;i=edge[i].nxt)
        if(!vis[edge[i].to]) dfs(edge[i].to);
}
int chk(int x)
{
    int tmp=INF;
    for(int i=1;i<=n;i++)
    {
        bfs(i);
        int num=0;
        if(dep[root]>x) continue;
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++)
            if(dep[i]<=x) vis[i]=1;
        for(int i=1;i<=n;i++)
            if(!vis[i])
            {
                len=0;dfs(i);
                num+=ceil_int(len,2*x+1);
            }
        tmp=min(tmp,num+1);
    }
    return tmp;
}
int main()
{
    freopen("holes.in","r",stdin);
    freopen("holes.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        d[u]++,d[v]++;
        add(u,v),add(v,u);
    }
    for(int i=1;i<=n;i++)
        if(d[i]>2) {root=i;break;}
    if(!root) {printf("%d",ceil_int(n-k,2*k));return 0;}
    int l=1,r=n,ans=INF;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(chk(mid)<=k) ans=min(ans,mid),r=mid-1;
        else l=mid+1;
    }
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/water-radish/p/9337902.html