1 条题解

  • 0
    @ 2025-8-24 21:56:57

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar zhoutb2333
    **

    搬运于2025-08-24 21:56:57,当前版本为作者最后更新于2018-01-12 13:38:08,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    暴力搜索预期得分3030分左右。

    状压预期得分7070分左右。

    考虑费用流,将剩余不稳定度和最小转为消除不稳定度和最大。

    首先拐角处只能放在有不稳定度的格子上:如果它的拐角处放在了X+YX+Y为偶数的格子上,那么它不仅没有减少不稳定度,而且还占地。这个贪心显然正确。

    那么黑白染色,X+YX+Y为偶数的点分一类,为奇数的分一类。将LL形柱子抽象成两个非拐角处的格子的路径(这两个格子的X+YX+Y都为偶数,拐角处的第三个格子的X+YX+Y为奇数)。那么这两个格子肯定一个在奇数列一个在偶数列,证明显然。

    于是建图:

    • 一共有四列点。

    • X+Y为偶数的点再分为两类:奇数列的和偶数列的。

    • 奇数列的点放在左边(第一列),源点向每个点连一条容量为1,费用为0的边。

    • 偶数列的点放在右边(第四列),每个点向汇点连一条容量为1,费用为0的边。

    • X+Y为奇数的点每个点拆开,一分为二,分别放在第二列和第三列。第二列的每个点向第三列的自己连一条容量为1,费用为负的该点的权值(不稳定度)。

    • 如果第一列(X+Y为偶数,奇数列的点)和第二列的点相邻,连一条容量为1,费用为0的边,第三、四列同理。

    • 当然塌了的格子不能连边。

    跑一遍最小费用最大流即可。这样建图每次增广的流都肯定为11,所以可以在最大流为MM的时候直接跳出。期望得分4040分。

    (???)

    因为这样是错的,

    4 4 6
    0 1 0 1000
    0 0 0 0
    0 1 0 1
    0 0 0 0
    1 1
    2 1
    2 3
    4 1
    4 3
    4 4
    

    就是一个构造的反例。我们不能先保证总流量最大再保证总费用最小,而是要尽可能地让总费用最小。也就是说我们要把柱子用在刀刃上,而不是放越多越好。于是想到如果本次增广源点到汇点的费用为正,直接跳出即可。(我的构造方式是连负费用边,如果连正权边跑最长路那么费用为负跳出即可)

    彩蛋:

    额,我不太会构造这样的数据,就把这一小片乘以1000贴在后面每个数据的左上角了。所以这么写也可以AC:printf("%d\n",sum+mincost-998000);

    额我在说什么...

    这样就可以100100分,复杂度O(O(费用流))

    #include<bits/stdc++.h>
    #define maxn 105
    #define maxe 100010
    #define INF 1<<30
    using namespace std;
    
    int head[maxe],nxt[maxe],point[maxe],flow[maxe],val[maxe],tot=1;
    int pre[maxe],preedge[maxe],tmpflow[maxe],dis[maxe],s=0,t=100000,E,ofs=25000;
    int v[maxn][maxn],sum,n,m,k;
    bool in[maxe];
    queue<int> q;
    int get(int x,int y,int lvl){
        return (x-1)*n+y+ofs*lvl;
    }
    bool chk(int num){
        num%=ofs;
        int x=(num-1)/n+1,y=(num-1)%n+1;
        if(x<1||y<1||x>n||y>n||v[x][y]==-1)
            return false;
        return true; 
    }
    void ADD(int x,int y,int f,int c){
        val[++tot]=c;
        flow[tot]=f;
        point[tot]=y;
        nxt[tot]=head[x];
        head[x]=tot;
    }
    void add(int x,int y,int f,int c){
        if((x!=s&&x!=t&&!chk(x))||(y!=s&&y!=t&&!chk(y)))
            return;
        ADD(x,y,f,c),ADD(y,x,0,-c);
    }
    bool bfs(){
        memset(dis,0x3f,sizeof(dis));
        dis[s]=0,in[s]=true,q.push(s),tmpflow[s]=m;
        while(!q.empty()){
            int x=q.front();
            in[x]=false;
            q.pop();
            for(int j=head[x];j;j=nxt[j]){
                if(!flow[j]||dis[point[j]]<=dis[x]+val[j])
                    continue;
                dis[point[j]]=dis[x]+val[j];
                pre[point[j]]=x;
                preedge[point[j]]=j;
                tmpflow[point[j]]=min(tmpflow[x],flow[j]);
                if(!in[point[j]])
                    in[point[j]]=true,q.push(point[j]);
            }
        }
        return dis[t]!=0x3f3f3f3f;
    }
    int main(){
        int x,y,mincost=0;
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&v[i][j]),sum+=v[i][j];
        for(int i=1;i<=k;i++)
            scanf("%d%d",&x,&y),v[x][y]=-1;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++){
                if((i+j)%2)
                    add(get(i,j,1),get(i,j,2),1,-v[i][j]);
                else{
                    if(j%2){
                        add(s,get(i,j,0),1,0);
                        add(get(i,j,0),get(i,j-1,1),1,0);
                        add(get(i,j,0),get(i,j+1,1),1,0);
                        add(get(i,j,0),get(i-1,j,1),1,0);
                        add(get(i,j,0),get(i+1,j,1),1,0);
                    }
                    else{
                        add(get(i,j,3),t,1,0);
                        add(get(i,j-1,2),get(i,j,3),1,0);
                        add(get(i,j+1,2),get(i,j,3),1,0);
                        add(get(i-1,j,2),get(i,j,3),1,0);
                        add(get(i+1,j,2),get(i,j,3),1,0);
                    }
                }
            }
        while(bfs()&&m){
            if(dis[t]>0)
                break;
            int k=t;
            while(k!=s){
                flow[preedge[k]]-=tmpflow[t];
                flow[preedge[k]^1]+=tmpflow[t];
                k=pre[k];
            }
            m-=tmpflow[t];
            mincost+=dis[t]*tmpflow[t];
        }
        printf("%d\n",sum+mincost);
        return 0; 
    } 
    
    • 1

    信息

    ID
    3033
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者