码迷,mamicode.com
首页 > Windows程序 > 详细

[APIO2015]题解

时间:2015-05-23 21:13:46      阅读:1474      评论:0      收藏:0      [点我收藏+]

标签:

说实话今年的APIO不是太难

但仍然阻止不了我酱油

26分TAT

巴厘岛的雕塑、巴邻旁之桥暴力

摩天楼那题SPFA莫名写跪了

第一题知道是动规不会写方程TAT

膜拜cstdio的位运算

第一题 巴厘岛的雕塑

题目大意是N个数,分为[A,B]个组

使得每组求和后异或和最小

话说为什么是最小TAT,强行黑一波印尼政府

9分算法

每两个之间判断放不放隔板,时间复杂度O(2^n)

71分算法

考虑贪心

从答案(二进制)的最高位到最低位,逐位判断是否能为0

定义状态f[i][j]

表示前i个数分成j位满足前答案前k-1位的是否可行,且k=1

然后枚举区间[A,B],判断是否对于A<=i<=B,存在f[n][i]=true,如果存在,则该位可以为0

时间复杂度O(log2M*N^3)

M为答案十进制长度

100分算法

我们注意到最后一组数据A=1,也就是没有下限,于是我们用一维数组F[i]

代替f[i][j],

F[i]表示前i个数最少要分为几组,最后判断是否F[n]<=B即可

    #include <fstream>
    //#include <iostream>
    #include <cstring>
    #define LL long long
    using namespace std;
    ifstream cin("sculpture.in");
    ofstream cout("sculpture.out");
    int n,a,b;
    int A[2001]={0};
    bool f[2001][2001]={0};
    long long S[2001]={0};
    long long limit;
    int F[2001]={0};
    long long bark(int x)
    {
        long long ans=1;
        ans=ans<<x;
        //cout<<ans<<endl;
        return ans;
    }
    bool mark()
    {
        int i,j,k;
        memset(f,0,sizeof(f));
        f[0][0]=1;
        for(i=0;i<n;i++)
        {
            for(j=0;j<b;j++)
            {
                if(f[i][j])
                {
                    for(k=i+1;k<=n;k++)
                    {
                        if(((S[k]-S[i])|limit)==limit)
                        {
                            f[k][j+1]=1;
                        }
                    }
                }
            }
        }
        //cout<<a<<‘ ‘<<b<<endl;
        for(k=a;k<=b;k++)if(f[n][k])return 1;
        return 0;
    }
    bool dark()
    {
        int i,j,k;
        memset(F,63,sizeof(F));
        F[0]=0;
        for(i=0;i<n;i++)
        {
            for(k=i+1;k<=n;k++)
            {
                if(((S[k]-S[i])|limit)==limit)
                {
                    F[k]=min(F[k],F[i]+1);
                }
            }
        }
        return F[n]<=b;
    }
    void judge(int x)
    {
        bool OK=0;
        limit-=bark(x);
        if(a!=1)OK=mark();
        if(a==1)OK=dark();
        //cout<<OK<<endl;
        if(!OK)limit+=bark(x);
    }
    int main()
    {
        int i,j,k;
        cin>>n>>a>>b;
        for(i=1;i<=n;i++)cin>>A[i];
        for(i=1;i<=n;i++)S[i]=S[i-1]+A[i];
        //for(i=1;i<=n;i++)cout<<S[i]<<‘ ‘;
        limit=bark(46)-1;
        //cout<<limit<<endl;
        for(i=45;i>=0;i--)judge(i);
        cout<<limit<<endl;
        return 0;
    }

第二题 雅加达的摩天楼

题目大意:

有N个建筑物,M只狗

告诉每只狗的初始建筑物位置b

和跳跃能力p

每只狗一步可以调到b+p或b-p的建筑物上

求0号狗到1号狗最少需要的跳跃步数

 

很容易看出这题是一个最短路径问题

关键在建边

10分算法

强行一波DFS(尼玛比正解还难打估计没人打)

22分算法

把每只狗能到达的所有位置建边,边的数量会爆

57分算法

稍微加一个优化即可,只与有狗的建筑物建边

100分算法(伪)

由于题目数据略水,不需要加什么特别强的优化

去掉同一个建筑物上跳跃能力相同的狗,对某些数据可以大大加快速度

97分算法(UOJ)

/*以下内容本人并不会,摘自http://blog.csdn.net/regina8023/article/details/45766241,感谢作者

考虑分块

对于跳跃能力大于sqrt(n)的狗,我们直接建边

对于跳跃能力小于sqrt(n)的狗,

我们可以在后面添加一些辅助点来实现预处理:
枚举这sqrt(n)个长度,在后面分别建n个辅助点,连向前面对应的位置,同时连向可以来自和到达的辅助点。

对于读入直接连向他对应的辅助点即可。*/

对于最短路径的求法,我推荐Dij,因为本题十分特殊,边的数量远远大于点的数量,用SPFA较快

可是我不知道为什么我的加优先队列的DIJ会挂一组

最后还是用了SPFA,注:我下面的程序没有分块

#include <fstream>
#include <queue>
#include <vector>
#include <algorithm>
#include <string.h>
#include <map>
using namespace std;
ifstream cin("skyscraper.in");
ofstream cout("skyscraper.out");
int n,m;
bool l[30001]={0};
vector<int> D[30001],G[30001],C[30001],A,Q,E[30001];
int len[30001]={0};
int f[30001]={0};
bool visit[30001]={0};
int INF=9999999;
class node
{
public:
    int x;
    int y;
}ab[30001];
map<node,bool>F;
void SPFA(int s) { // SPFA
    queue<int> q;
    l[s] = 1;
    fill_n(f, n, INF); f[s] = 0;
    q.push(s);
    while(!q.empty()) {
        int u = q.front(); q.pop();
        l[u] = 0;
        for(int i=0; i<G[u].size(); i++) {
            int v = G[u][i];
            if(f[v] > f[u] + C[u][i]) {
                f[v] = f[u] + C[u][i];
                if(!l[v]) {
                    l[v] = 1;
                    q.push(v);
                }
            }
        }
    }
}
int main()
{
    int i,j,k,a,b,start,end;
    cin>>n>>m;
    for(i=1;i<=m;i++)
    {
        cin>>ab[i].x>>ab[i].y;
        visit[ab[i].x]=1;
    }
    for(i=1;i<=m;i++)
    {
        a=ab[i].x;
        b=ab[i].y;
        int c=ab[i-1].x;
        int d=ab[i-1].y;
        if(a==c&&b==d&&i>=10)continue;
        for(j=a+b;j<=n;j+=b)
        {
            //cout<<j<<‘ ‘<<endl;
            if(!visit[j])continue;
            G[a].push_back(j);
            C[a].push_back((j-a)/b);
        }
        for(j=a-b;j>=0;j-=b)
        {
            if(!visit[j])continue;
            G[a].push_back(j);
            C[a].push_back((a-j)/b);
        }
        if(i==1)start=a;
        if(i==2)end=a;
    }
    //memset(l,sizeof(l),0);
    SPFA(start);
    if(start==end)cout<<0<<endl;
    else if(f[end]!=INF)cout<<f[end]<<endl;
    else cout<<-1<<endl;
    return 0;
}

第三题:巴邻旁之桥

题目大意比较麻烦,这里就不说了

家和办公室的同一侧的,预处理提前算好

我们可以通过暴力程序和或手算发现,

当k=1时,我们目标是最小化|a-x|+|b-x|

不难发现答案是所有家和办公室的中位数(考场上只顾着打暴力啦)

/*以下内容本人还没有掌握,摘自http://www.bubuko.com/infodetail-809274.html

对于k=2时

按照每个人的家和办公室的中点排序后,一定存在一个分割点使得前缀都走左边的桥,后缀都走右边的桥(因为走靠近中点的桥不会更差)。

于是我们枚举分割点,离散化后用权值线段树动态维护两个区间的中位数求解即可。*/然而我并不会写线段树

目前我只拿到了31分

31分算法

//May 20 2015
//By Satoshi
//#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <map>
#define LL long long
using namespace std;
ifstream cin("cstdiorank1AK.in");
ofstream cout("cstdiorank1AK.out");
int aa[100001]={0},bb[100001]={0};
int cc[200001]={0};
LL ans=0;
int n,K;
map<int,bool>F;
vector<int> q;
void work1()
{
    int pos,i;
    pos=cc[n];
    //out<<pos<<endl;
    for(i=1;i<=n;i++)
    {
        //out<<ans<<endl;
        ans+=abs(pos-bb[i])+abs(pos-aa[i])+1;
    }
}
void work2()
{
    int i,j,k;
    LL best=1LL<<51;
    LL sum=0;
    LL u,v,w;
    for(i=0;i<q.size()-1;i++)
    {
        for(j=i+1;j<q.size();j++)
        {
            sum=0;
            for(k=1;k<=n;k++)
            {
                u=abs(aa[k]-q[i])+abs(bb[k]-q[i]);
                v=abs(aa[k]-q[j])+abs(bb[k]-q[j]);
                w=min(u,v)+1;
                sum+=w;
            }
            if(sum<best)best=sum;
        }
    }
    ans+=best;
}
int main()
{
    char a,c;
    int i,j;
    int b,d;
    int size=0;
    cin>>K>>n;
    for(i=1;i<=n;i++)
    {
        cin>>a>>b>>c>>d;
        if(a==c)ans+=abs(b-d);
        else
        {
            size++;
            aa[size]=b;
            bb[size]=d;
        }
        if(F[b]==0)
        {
            q.push_back(b);
            F[b]=1;
        }
        if(F[d]==0)
        {
            q.push_back(d);
            F[d]=1;
        }
    }
    n=size;
    //out<<ans<<endl;
    for(i=1;i<=n;i++)
    {
        cc[i]=aa[i];
        cc[n+i]=bb[i];
    }
    sort(cc+1,cc+2*n+1);
    //for(i=1;i<=n*2;i++)out<<cc[i]<<‘ ‘;
    if(K==1)work1();
    if(K==2)work2();
    //for(i=0;i<q.size();i++)out<<q[i]<<‘ ‘;
    
    cout<<ans<<endl;
    return 0;
}

如有错误请指出,谢谢(^_^)

[APIO2015]题解

标签:

原文地址:http://www.cnblogs.com/Satoshi/p/4524805.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!