1 条题解

  • 0
    @ 2025-8-24 22:18:50

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar getchar123
    即使遍体鳞伤,也永远不会服软

    搬运于2025-08-24 22:18:50,当前版本为作者最后更新于2020-06-02 14:46:44,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    问题转化

    可以将问题转化为:

    给定kk个十形板的中心,要将每个十形板删掉一个角,使所有十形板不重叠,并且覆盖的数字之和尽可能大。(十形板如下图)

    有解性判定

    先考虑如何判断有解性。

    将每个十形板看做一个点,两个十形板相交的地方看做边,如下图。

    当一块板和另一块板相交的地方在它们中心时,由于中心是不能被删除的,所以两个点各连一条自环(一块板的一个角在地图外时同理)。

    当两块以上的板相交在同一个点时,连成环是错误的,因为一个格子只能看做一条边。

    现在问题变成:能否找到一种方案,使每条边匹配到一个点(一个点不能匹配两条边,但可以匹配0条边)。

    一个结论:对于每个连通块,当且仅当它是一棵树或一棵基环树时有解。(手模会发现显然)

    求最优解

    接下来求最优解就很简单了。

    当一个连通块是基环树时,每块板都已经被删去一个角,直接每个被覆盖的格子统计一遍就行。

    当一个连通块是树时,有一块板还没有删角,因此执行完基环树的步骤后找出其中一个被覆盖的不是特殊格子的权值最小的格子删去即可。

    最后所有连通块答案的和即为最终结果。时间复杂度O(n)O(n)

    代码

    //马蜂比较丑,建议把重点放在注释上 
    #include<bits/stdc++.h>
    using namespace std;
    int n,m;
    vector<int>ma[1000010],maa[1000010];
    //由于n和m的范围是不确定的,所以要用vector(map会T飞) 
    int d[1000010],tail=1,K,jg,ts[1000010][2];
    int qwq[1000010];//用于存储每个格子周围的最小值 
    bool vv[1000010];
    int xx[4]={1,0,-1,0},yy[4]={0,1,0,-1};
    struct bbbb{
    	int ne,to;
    	bool vis;
    }b[8000010];
    void jb(int x,int y){
    	tail++;
    	b[tail].ne=d[x];
    	b[tail].to=y;
    	b[tail].vis=0;
    	d[x]=tail;
    	return;
    }
    queue<int>q;
    int bfs(int x){//遍历连通块,数点数和边数 
    	int cnt=1,cnt1=0,minn=qwq[x];//cnt1:边数  cnt:点数 
    	vv[x]=1;
    	q.push(x);
    	while(q.empty()==false){
    		int xxx=q.front();q.pop();
    		for(int i=d[xxx];i;i=b[i].ne){
    			int y=b[i].to;
    			if(b[i].vis==1)continue;
    			cnt1++;b[i].vis=b[i^1].vis=1;
    			if(vv[y]==1)continue;
    			vv[y]=1;cnt++;
    			q.push(y);minn=min(minn,qwq[y]);
    		}
    	}
    	if(cnt<cnt1)return -1;
    	if(cnt==cnt1)return 0;
    	return minn;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++){//输入地图 
    		ma[i].push_back(0);
    		maa[i].push_back(0);
    		for(int j=1;j<=m;j++){
    			int x;
    			scanf("%d",&x);
    			ma[i].push_back(x);
    			maa[i].push_back(0);
    		}
    	}
    	scanf("%d",&K);
    	for(int i=1;i<=K;i++){
    		scanf("%d%d",&ts[i][0],&ts[i][1]);
    		ts[i][0]++;ts[i][1]++;
    		maa[ts[i][0]][ts[i][1]]=-i;//标记每个块的中心点 
    	}
    	for(int i=1;i<=K;i++){
    		qwq[i]=1e9;
    		jg+=ma[ts[i][0]][ts[i][1]];		
    		for(int j=0;j<4;j++){
    			int x=ts[i][0]+xx[j],y=ts[i][1]+yy[j];
    			if(x<=0||x>n||y<=0||y>m||maa[x][y]<0){//在地图外或其它块的中心 
    				jb(i,i);jb(i,i);//连自环 
    			}
    			else{
    				if(maa[x][y]>0){//角相交 
    					jb(i,maa[x][y]);
    					jb(maa[x][y],i);
    				}
    				else{
    					jg+=ma[x][y];
    					qwq[i]=min(qwq[i],ma[x][y]);
    				}
    				maa[x][y]=i;
    			}
    		}
    	}
    	int flag=0;
    	for(int i=1;i<=K;i++){
    		if(vv[i]==0){
    			int qaqa=bfs(i);
    			if(qaqa==-1){
    				flag=1;
    				break;
    			}
    			jg-=qaqa;
    		}		
    	}
    	if(flag==1){
    		puts("No");
    		return 0;
    	}
    	cout<<jg;
    	return 0;		
    }
    

    完结撒花~

    p.s.不知道为什么窝的代码跑得那么慢,难道是没写快读(?)

    如果有哪里没说清楚的可以随时找窝提问、提建议w~

    • 1

    信息

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