[2020年百度之星·程序设计大赛-初赛一]Coda的题解集

可能是打智(ka)算(chang)之(da)道(sai)留下的后遗症,看见Rank掉了,想把A掉的题输入优化一下再交一遍,然后就恰了一发罚时,从Rank900掉到了Rank1100…


Drink

纯模拟,没什么好说的。

#include
const int inf=0x3f3f3f3f;
using namespace std;
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t,n,m,x,y,tmp,ans;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        ans=inf;
        for(int i=1;i<=n;i++)
        {
            cin>>x>>y;
            tmp=m/x*y;
            if(m%x)
                tmp+=y;
            ans=min(ans,tmp);
        }
        cout<<ans<<endl;
    }
}

GPA

dfs,取每个分数段里最低的,最终搜索深度为4时,只要用掉的分数不超过输入的分数就行,取一个最大结果输出。

#include
const int inf=0x3f3f3f3f;
using namespace std;
int t,score;
double ans;
void dfs(double s,double g,int c)
{
    if(c>4) return;
    if(s>score) return;
    if(c==4&&s<=score)
    {
        ans=max(ans,g);
        return;
    }
        dfs(s,g,c+1);
        dfs(s+60,g+1,c+1);
        dfs(s+62,g+1.7,c+1);
        dfs(s+65,g+2,c+1);
        dfs(s+67,g+2.3,c+1);
        dfs(s+70,g+2.7,c+1);
        dfs(s+75,g+3,c+1);
        dfs(s+80,g+3.3,c+1);
        dfs(s+85,g+3.7,c+1);
        dfs(s+90,g+4,c+1);
        dfs(s+95,g+4.3,c+1);
}
int main()
{
    cin>>t;
    while(t--)
    {
        ans=0;
        cin>>score;
        dfs(0,0,0);
        cout<<fixed<<setprecision(1)<<ans<<endl;
    }
}

Dec

暴力美学
dp,一开始尝试从(a,b)往(1,1)跑,但是因为有多组测试数据,每组的起点不同导致跑出来的结果在各组数据间不能互通,最后试了一下反着跑,因为起点都是(1,1),所以dp出来的数据可以通用。开局直接从(1,1)跑到(1000,1000),然后打表就完事了。
一开始T了,加了输入优化卡进了时限,900ms还行。

#include
const int inf=0x3f3f3f3f;
using namespace std;
int mem[1010][1010];
int gcd(int a,int b)
{
    return b ? gcd(b,a%b) : a;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    for(int i=1;i<=1000;i++)
        for(int j=1;j<=1000;j++)
            if(gcd(i,j)==1)
                mem[i][j]=max(mem[i-1][j],mem[i][j-1])+1;
            else
                mem[i][j]=max(mem[i-1][j],mem[i][j-1]);
    int t,a,b;
    cin>>t;
    while(t--)
    {
        cin>>a>>b;
        cout<<mem[a][b]<<endl;
    }
}

Civilization

纯模拟,枚举建城市的点,w用来存从(x,y)跑到(i,j)所用的回合数,work存(i,j)城市周围可用工作点的收益,因为城里始终要有一个人工作,所以城市点最优先,其次就是周围的点从大到小。这里加了一个前缀和(代码中变量为k的循环),每次加食物的时候就不用从1开始遍历了。

#include
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int maxn=510;
int t,n,x,y,a[maxn][maxn],w[maxn][maxn],work[maxn][maxn][27],ans;
void solve(int round,int people,int food,int xx,int yy)
{
    round++;
    food+=work[xx][yy][people];
    while(food>=(8*people*people))
        people++;
    if(people==9)
    {
        ans=min(ans,round);
        return;
    }
    solve(round,people,food,xx,yy);
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>t;
    while(t--)
    {
        ans=inf;
        memset(a,0,sizeof(a));
        memset(w,0,sizeof(w));
        memset(work,0,sizeof(work));
        cin>>n>>x>>y;
        for(int i=1; i<=n; ++i)
            for(int j=1; j<=n; ++j)
            {
                cin>>a[i][j];
                int dis=abs(x-i)+abs(y-j);
                w[i][j]=(dis>>1)+(dis&1?1:0);

            }
        for(int i=1; i<=n; ++i)
            for(int j=1; j<=n; ++j)
            {
                int cnt=0;
                for(int ii=-3; ii<=3; ++ii)
                    for(int jj=-3; jj<=3; ++jj)
                        if((abs(ii)+abs(jj))<=3&&i+ii>=1&&i+ii<=n&&j+jj>=1&&j+jj<=n)
                        {
                            work[i][j][++cnt]=a[i+ii][j+jj];
                            if(ii==0&&jj==0)
                                swap(work[i][j][cnt],work[i][j][1]);
                        }
                sort(&work[i][j][2],&work[i][j][26],greater<int>());
                for(int k=2; k<=9; ++k)
                    work[i][j][k]+=work[i][j][k-1];
                solve(w[i][j],1,0,i,j);
            }
        cout<<ans<<endl;
    }
}

Rotate

每层的黑块数目单调,无论递增或递减,都是形成一个森林,森林的连通块数量为总点数-总边数。在这题中,相邻的两个圈中,如果有内外黑块边界相接,就形成了一条边,连通块数目就是总黑块数-总边数。每个圈中有一半是黑块,总黑块数=a[1]/2+a[2]/2+…+a[n]/2。对于任意一对相邻的环,设外环有x块,内环有y块,且根据题意x

#pragma GCC optimize(3)
#include
typedef long long ll;
const int maxn=11;
const int mod=1e9+7;
int t,n,a[maxn];
inline int redn()
{
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-f;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
ll mod_pow(ll x,ll n)
{
    ll res=1;
    while(n>0)
    {
        if(n&1)
            res=res*x%mod;
        x=x*x%mod;
        n>>=1;
    }
    return res;
}
signed main()
{
    t=redn();
    while(t--)
    {
        n=redn();
        for(int i=1;i<=n;++i)
            a[i]=redn();
        printf("%lld\n",(a[1]+a[n])%mod*mod_pow(4,mod-2)%mod);
    }
}

Matrix

k的大小达到了1e10,O(k)的复杂度会炸,下面是参考的O(√k)算法博客,解释非常详细。
https://www.cnblogs.com/jianjinxiani/p/13361465.html

#pragma GCC optimize(3)
#include
#define int long long
const int maxn=6;
int t,len[maxn],k;
inline int redn()
{
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-f;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
int cntp(int xysum,int len)
{
    if(xysum>2*len||!len) return 0;
    if(!xysum) return 1;
    if(xysum<=len) return 4*xysum;
    return (2*len-xysum+1)*4;
}
signed main()
{
    t=redn();
    while(t--)
    {
        int ans(0),w(0);
        len[4]=redn();
        len[3]=redn();
        len[2]=redn();
        len[1]=redn();
        k=redn();
        while(k)
        {
            for(int i=1;i<=4;++i)
            {
                if(w%i) continue;
                int cnt=cntp(w/i,len[i])-cntp(w/i,len[i+1]);
                ans+=std::min(cnt,k)*w;
                k-=std::min(cnt,k);
            }
            ++w;
        }
        printf("%lld\n",ans);
    }
}

Mosquito

首先,如果蚊子总数少于n* m直接特判输出-1,然后对时间t二分答案,其中对每个t跑一次网络流。直接的思路是源点S连接每个窗户,边容量为各窗户的蚊子数,再从每个窗户向从此窗户出发的t时间内能到达的每个位置连接容量为inf的边,最后从每个窗户到汇点T连接容量为1的边。如果这样建图,在最坏的情况下有nmk+2个点,建图和跑网络流消耗巨大,可以观察到窗户个数k很少,我们可以通过二进制状压,压缩每个格点与所有窗户的连接情况(从第0位开始,若第i位为1,即表示t时间内蚊子可以从第i个窗户到达该点),总状态数只有2^k个,相当于把n *m个点中状态相同的点合并了,且把与汇点连边的容量合并了。

#pragma GCC optimize(3)
#include
using namespace std;
const int maxv=100;
const int inf=0x3f3f3f3f;
int cas,n,m,k,sumz,cnt[130];
struct window
{
    int x,y,z;
}w[10];
struct edge
{
    int to,cap,rev;
};
vector<edge> g[maxv];
int level[maxv],iter[maxv];
void add_edge(int from,int to,int cap)
{
    g[from].push_back((edge){to,cap,g[to].size()});
    g[to].push_back((edge){from,0,g[from].size()-1});
}
void bfs(int s)
{
    memset(level,-1,sizeof(level));
    queue<int> que;
    level[s]=0;
    que.push(s);
    while(!que.empty())
    {
        int v=que.front();que.pop();
        for(int i=0;i<g[v].size();++i)
        {
            edge &e=g[v][i];
            if(e.cap>0&&level[e.to]<0)
            {
                level[e.to]=level[v]+1;
                que.push(e.to);
            }
        }
    }
}
int dfs(int v,int t,int f)
{
    if(v==t) return f;
    for(int &i=iter[v];i<g[v].size();++i)
    {
        edge &e=g[v][i];
        if(e.cap>0&&level[v]<level[e.to])
        {
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0)
            {
                e.cap-=d;
                g[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s,int t)
{
    int flow=0;
    while(true)
    {
        bfs(s);
        if(level[t]<0) return flow;
        memset(iter,0,sizeof(iter));
        int f;
        while((f=dfs(s,t,inf))>0)
            flow+=f;
    }
}
inline int redn()
{
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-f;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
int solve(int t)
{
    memset(cnt,0,sizeof(cnt));
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
        {
            int state=0;
            for(int num=0;num<k;++num)
                if(t>=abs(i-w[num].x)+abs(j-w[num].y))
                    state=state|1<<num;
            ++cnt[state];
        }
    int M=pow(2,k);
    for(int i=0;i<=M+k+1;++i)
        g[i].clear();
    for(int i=0;i<k;++i)
        add_edge(M+k,M+i,w[i].z);
    for(int i=0;i<M;++i)
    {
        add_edge(i,M+k+1,cnt[i]);
        for(int j=0;j<k;++j)
            if(i&1<<j)
                add_edge(M+j,i,inf);
    }
    int flow = max_flow(M+k,M+k+1);
    return flow==n*m;
}
signed main()
{
    cas=redn();
    while(cas--)
    {
        sumz=0;
        n=redn();
        m=redn();
        k=redn();
        for(int i=0;i<k;++i)
        {
            w[i].x=redn();
            w[i].y=redn();
            w[i].z=redn();
            sumz+=w[i].z;
        }
        if(sumz<n*m)
        {
            printf("-1\n");
            continue;
        }
        int lb=0,ub=n*m-2;
        while(ub-lb>1)
        {
            int mid=(lb+ub)/2;
            if(solve(mid)) ub=mid;
            else lb=mid;
        }
        printf("%d\n",ub);
    }
}

Function

莫比乌斯反演,我不会。

你可能感兴趣的:([2020年百度之星·程序设计大赛-初赛一]Coda的题解集)