[APIO2013]

A.机器人

题目大意:给定一个n*m的地图,有一些障碍物和k个机器人,你每次可以选择一个机器人往任意一个方向推,遇到转向器会转向,两个编号相邻的机器人可以合并,求最少推多少次可以全部合并。

$n,m\leqslant 500 k\leqslant 9$

题解:用f[i][j][k][l]表示i到j的机器人在(k,l)合并的最小次数,那么当(k',l')推一次可以到(k,l)的时候,它可以从f[i][j][k'][l']+1转移。当然,它还可以从f[i][p][k][l]+f[p+1][j][k][l]转移,所以我们先计算出

所有点推一次到哪里,每次做完一个f[i][j]之后,就做一次spfa。

但是spfa会T,并不能过。我们考虑开两个队列,在spfa之前把起始的位置拍好序扔到队列1,然后每次取出两个队列队头较小的松弛,松弛到的点加入队列2,这样可以保证队列1和队列2有序,spfa就进化为了bfs,排序最好用O(n)的计数排序,跑的飞快。

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define INF 32639
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

unsigned short d[11][11][501][501];int mark[501][501][4],top=0;
int n,h,w,cnt=0,L,R;
int v[180005],sa[180005];
char st[501][501];
struct P{short x,y;
    P(short x=0,short y=0):x(x),y(y){}
    bool operator !(){return x==0&&y==0;}
}pos[11],to[505][505][4],q[180005];
bool b[501][501];
const int dis[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
queue<P> q1,q2;

P dfs(int x,int y,int dir)
{    
    if(mark[x][y][dir]==cnt) return to[x][y][dir]=P(-1,-1);mark[x][y][dir]=cnt;
    if(!(!to[x][y][dir])) return to[x][y][dir];    
    int pre=dir;
    if(st[x][y]=='A') dir=(dir+3)%4;
    if(st[x][y]=='C') dir=(dir+1)%4;
    int xx=x+dis[dir][0],yy=y+dis[dir][1];
    if(xx<1||yy<1||xx>h||yy>w||st[xx][yy]=='x') return to[x][y][pre]=P(x,y);
    return to[x][y][pre]=dfs(xx,yy,dir);
}

void spfa()
{
    memset(v,0,sizeof(v));
    for(int i=1;i<=top;i++)v[d[L][R][q[i].x][q[i].y]]++;
    for(int i=1;i<=INF;i++)v[i]+=v[i-1];
    for(int i=1;i<=top;i++) sa[v[d[L][R][q[i].x][q[i].y]]--]=i;
    for(int i=1;i<=top;i++) q1.push(q[sa[i]]); 
    top=0;
    while((!q1.empty())||(!q2.empty()))
    {
        P now;
        if(q1.empty()||((!q2.empty())&&d[L][R][q1.front().x][q1.front().y]>d[L][R][q2.front().x][q2.front().y]))
            now=q2.front(),q2.pop();
        else 
            now=q1.front(),q1.pop();
        b[now.x][now.y]=0;
        for(int i=0;i<4;i++)
        {
            P t=to[now.x][now.y][i];         
            if(t.x==-1||t.y==-1) continue;    
            if(d[L][R][now.x][now.y]+1<d[L][R][t.x][t.y])
            {
                d[L][R][t.x][t.y]=d[L][R][now.x][now.y]+1;
                if(!b[t.x][t.y])
                {
                    b[t.x][t.y]=1;
                    q2.push(t);
                }
            }
        }

    }
}

int main()
{
    n=read();w=read();h=read();
    memset(d,127,sizeof(d));
    for(int i=1;i<=h;i++)
    {
        scanf("%s",st[i]+1);
        for(int j=1;j<=w;j++)
            if(st[i][j]>'0'&&st[i][j]<='9')
                pos[st[i][j]-'0']=P(i,j);
    }
    for(int i=1;i<=h;i++)
        for(int j=1;j<=w;j++)
            if(st[i][j]!='x')
                for(int k=0;k<4;k++)
                    ++cnt,dfs(i,j,k);
    for(int i=1;i<=n;i++)
    {
        b[pos[i].x][pos[i].y]=1;q[++top]=pos[i];
        L=R=i;d[i][i][pos[i].x][pos[i].y]=0;spfa();
    }
    for(int l=2,j;l<=n;l++)
        for(int i=1;(j=i+l-1)<=n;i++)
        {
            for(int x=1;x<=h;x++)
                for(int y=1;y<=w;y++)
                {
                    for(int k=i;k<j;k++)
                        d[i][j][x][y]=min(d[i][j][x][y],(unsigned short)(d[i][k][x][y]+d[k+1][j][x][y]));
                    if(d[i][j][x][y]<INF)
                        q[++top]=P(x,y),b[x][y]=1;
                }
            L=i;R=j;spfa();
        }
    unsigned short ans=30000;
    for(int i=1;i<=h;i++)
        for(int j=1;j<=w;j++)
            ans=min(ans,d[1][n][i][j]);
    if(ans<30000) printf("%u\n",ans);
    else puts("-1");
    return 0;
}

 B.[Apio2013]道路费用

 

 $n\leqslant 10^{5},m\leqslant 3*10^{5},k\leqslant 20$

题解:强制选这K条边,然后我们发现K条边把整个图割成了K+1块,每一块内选的边肯定是相同的。我们把这几块缩点,然后枚举每条边选不选,对每种情况,暴力算出每条边的最大长度,通过dp算出这种情况的答案就可以啦。

复杂度$mlogm+k^{2}*2^{k}$

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define INF 200000000000000000LL
#define MN 100000
#define MM 300000
#define MK 20
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

struct edge{
    int from,to;ll w;int kind;
}e[MM+MK+5],ek[MK+5],e3[MK+5]; 
struct nedge{int to,next,kind;}e2[MN*2+5];
ll ans=0,sum[MK+5],p[MN+5],val[MK+5],mn[MK+5];
int n,m,K,cnt=0,s[MN+5],head[MN+5],cn=0,bel[MN+5],dep[MK+5],fa2[MK+5];
bool mark[MN+5],b[MK+5];

bool cmp(edge x,edge y){return x.w<y.w||(x.w==y.w&&x.kind>y.kind);}
int find(int x)
{
    return !s[x]?x:s[x]=find(s[x]);
}

void ins(int f,int t,int kind=0)
{
    e2[++cnt]=(nedge){t,head[f],kind};head[f]=cnt;
    e2[++cnt]=(nedge){f,head[t],kind};head[t]=cnt;
}

void kruscal()
{
    for(int i=1;i<=K;i++)
    {
        int x=find(ek[i].from),y=find(ek[i].to);
        s[x]=y,ins(ek[i].from,ek[i].to,1);
    }
    for(int i=1;i<=m;i++)
    {
        int x=find(e[i].from),y=find(e[i].to);
        if(x!=y)s[x]=y,ins(e[i].from,e[i].to,0);
    }
}

void getv(int x)
{
    mark[x]=1;bel[x]=cn;val[cn]+=p[x];
    for(int i=head[x];i;i=e2[i].next)
        if(!mark[e2[i].to]&&!e2[i].kind)
            getv(e2[i].to);
}

void dp(int x,int fa)
{
    sum[x]=val[x];fa2[x]=fa;
    for(int i=head[x];i;i=e2[i].next)
        if(e2[i].to!=fa)
        {
            dep[e2[i].to]=dep[x]+1;
            dp(e2[i].to,x);sum[x]+=sum[e2[i].to];
        }
}
int tms=0;
void solve()
{
    ll tot=0;cnt=0;
    for(int i=1;i<=cn;i++)s[i]=0,head[i]=0,mn[i]=INF;
    for(int i=1;i<=K;i++)
        if(b[i])
        {
            int x=find(ek[i].from),y=find(ek[i].to);
            if(x==y)return;s[x]=y;ins(ek[i].from,ek[i].to);
        }
    tms++;
    for(int i=1;i<=K;i++)
    {
        int x=find(e3[i].from),y=find(e3[i].to);
        if(x!=y) s[x]=y,ins(e3[i].from,e3[i].to);
    }
    dp(1,0);
    for(int i=1;i<=K;i++)
    {
        int u=e3[i].from,v=e3[i].to;
        if(dep[u]<dep[v]) swap(u,v);
        while(dep[u]>dep[v]) mn[u]=min(mn[u],e3[i].w),u=fa2[u];
        while(u!=v)
        {
            mn[u]=min(mn[u],e3[i].w);
            mn[v]=min(mn[v],e3[i].w);
            u=fa2[u],v=fa2[v];
        }
    }
    for(int i=1;i<=K;i++) if(b[i])
    {
        int u=ek[i].from,v=ek[i].to;
        if(dep[u]<dep[v]) swap(u,v);
        tot+=1LL*mn[u]*sum[u];
    }
    ans=max(ans,tot);
}

void dfs(int x)
{
    if(x>K)
    {
        solve();
        return;
    }
    b[x]=1;dfs(x+1);
    b[x]=0;dfs(x+1);
}

int main()
{
    n=read();m=read();K=read();
    for(int i=1;i<=m;i++)
        e[i].from=read(),e[i].to=read(),e[i].w=read();
    for(int i=1;i<=K;i++)
        ek[i].from=read(),ek[i].to=read();
    for(int i=1;i<=n;i++)p[i]=read();
    sort(e+1,e+m+1,cmp);kruscal();
    for(int i=1;i<=n;i++)if(!mark[i]) ++cn,getv(i);
    cnt=0;
    for(int i=1;i<=cn;i++) s[i]=0;
    for(int i=1;i<=m+K;i++) if(!e[i].kind)
    {
        int x=bel[e[i].from],y=bel[e[i].to],f1=find(x),f2=find(y);
        if(f1!=f2) s[f1]=f2,e3[++cnt]=(edge){x,y,e[i].w,0};
    }
    for(int i=1;i<=K;i++) ek[i].from=bel[ek[i].from],ek[i].to=bel[ek[i].to];
    dfs(1);
    cout<<ans;
    return 0;
}

 

posted @ 2017-04-01 17:53  FallDream  阅读(690)  评论(0编辑  收藏  举报