• [BJWC2018] Kakuro


    一、题目

    点此看题

    二、解法

    我一开始一直想不出来,直接刚这个题实在是太复杂了,因为一开始就是不合法的。

    下次遇到复杂的题一定要想 调整法我再不往这个方向想我吔屎

    好了言归正传,我们先找一组可行的解,但不是最优的,我们想法设法地把他调整到最优。可行的解很容易找啊,把所有空格子改成 (1) ,所有线索改成格子的个数。但你可能要问如果有些格子不能调整怎么办呢?不要怕直接刚,如果我们最后能把这些格子调整回来那就没问题,而且这样做很方便。

    如果从网络流方面来考虑的话那么把费用放在边上,然后我们用网络流来调整。请注意这个条件:输入保证这个从格式上来说一定是个合法的Kakuro 谜题,即每一段连续的空格的左边或者上面的格子包含线索。因为现在的图是合法的,所以可以把空格子当成边,连接两个线索,这样源汇的一条路径增加 (1) 的流量就表示这些格子的数都增加 (1) ,那么我们这样建图:

    • 源点连横着的线索,设原来的数字的 (x) ,现在的数字是 (y) ,改变权值的花费是 (c) ,如果 (x>y) ,说明改小了,如果需要的话我们可以回退这个操作,也就是连一条容量 (x-y) 费用为 (-c) 的边。然后还可以暴力调大,那么连一条容量为 (inf) 费用为 (c) 的边。
    • 竖着的线索连汇点,和上面的连法同理。
    • 设格子连接的两个线索是 (i,j) ,那么 (i,j) 先连一条容量为 (x-y) 的费用为 (-c)(此时 (y=1)),然后类似地连一条容量为 (inf) 费用为 (c) 的边。

    大概的图就建好了,然后我们跑最小费用可行流就可以达到调整的目的了,也就是在 (costgeq 0) 时退出。还有一个悬而未决的问题,就是不能调整的点怎么办,如果我们把 (c) 设置为 (inf) ,那么费用流肯定会使用对应的 (-inf) 的边,那么这个点就被调整回原来的状态了。

    怎么判断无解呢?就是有些不能改的点没有被调整回原来的状态((-inf) 的边没有满流),或者是不能改的点增加了数字((inf) 的边有流量通过),那么都是不合法的。

    但我怎么还是感觉有点不对劲呢?可能我还需要多想一想

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    using namespace std;
    const int M = 1005;
    const int N = 35;
    const int inf = 1e9;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,tot,cnt,f[M],a[N][N],id1[N][N],id2[N][N];
    int ans,s,t,dis[M],lst[M],pre[M],in[M],flow[M];
    int r[N][N],c[N][N],o[N][N],up[N][N],down[N][N];
    struct edge
    {
    	int v,f,c,next;
    	edge(int V=0,int F=0,int C=0,int N=0) : v(V) , f(F) , c(C) , next(N) {}
    }e[M*M];
    void add(int u,int v,int c,int fl)
    {
    	e[++tot]=edge(v,fl,c,f[u]),f[u]=tot;
    	e[++tot]=edge(u,0,-c,f[v]),f[v]=tot;
    }
    int bfs()
    {
    	queue<int> q;
    	for(int i=0;i<=t;i++) dis[i]=1e18;
    	dis[s]=0;pre[s]=-1;flow[s]=inf;
    	q.push(s);
    	while(!q.empty())
    	{
    		int u=q.front();q.pop();
    		in[u]=0;
    		for(int i=f[u];i;i=e[i].next)
    		{
    			int v=e[i].v,c=e[i].c;
    			if(dis[v]>dis[u]+c && e[i].f>0)
    			{
    				dis[v]=dis[u]+c;
    				pre[v]=u;lst[v]=i;
    				flow[v]=min(flow[u],e[i].f);
    				if(!in[v]) in[v]=1,q.push(v);
    			}
    		}
    	}
    	return dis[t]<0;
    }
    int Abs(int x)
    {
    	return x>0?x:-x;
    }
    signed main()
    {
    	n=read();m=read();
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    		{
    			a[i][j]=read();
    			if(a[i][j]==1 || a[i][j]==3) id1[i][j]=++cnt;
    			if(a[i][j]==2 || a[i][j]==3) id2[i][j]=++cnt;
    		}
    	s=0;t=cnt+1;tot=1;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    		{
    			if(a[i][j]==1 || a[i][j]==3) r[i][j]=read();
    			if(a[i][j]==2 || a[i][j]==3) c[i][j]=read();
    			if(a[i][j]==4) o[i][j]=read();
    		}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    		{
    			if(a[i][j]==1 || a[i][j]==3)
    			{
    				int x=read(),k=i+1;
    				if(x==-1) x=inf;
    				while(k<=n && a[k][j]==4)
    					up[k++][j]=id1[i][j];
    				k=k-i-1;
    				ans+=Abs(k-r[i][j])*x;
    				if(r[i][j]>k) add(s,id1[i][j],-x,r[i][j]-k);
    				add(s,id1[i][j],x,inf);
    			}
    			if(a[i][j]==2 || a[i][j]==3)
    			{
    				int x=read(),k=j+1;
    				if(x==-1) x=inf;
    				while(k<=m && a[i][k]==4)
    					down[i][k++]=id2[i][j];
    				k=k-j-1;
    				ans+=Abs(k-c[i][j])*x;
    				if(c[i][j]>k) add(id2[i][j],t,-x,c[i][j]-k);
    				add(id2[i][j],t,x,inf);
    			}
    			if(a[i][j]==4)
    			{
    				int x=read();
    				if(x==-1) x=inf;
    				ans+=Abs(o[i][j]-1)*x;
    				if(o[i][j]>1) add(up[i][j],down[i][j],-x,o[i][j]-1);
    				add(up[i][j],down[i][j],x,inf);
    			}
    		}
    	while(bfs())
    	{
    		ans+=flow[t]*dis[t];
    		int zy=t;
    		while(zy!=s)
    		{
    			e[lst[zy]].f-=flow[t];
    			e[lst[zy]^1].f+=flow[t];
    			zy=pre[zy];
    		}
    	}
    	for(int i=2;i<=tot;i+=2)
    	{
    		if(e[i].c==inf && e[i^1].f>0)
    		{
    			puts("-1");
    			return 0;
    		}
    		if(e[i].c==-inf && e[i].f>0)
    		{
    			puts("-1");
    			return 0;
    		}
    	}
    	printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    如何以nobody用户执行命令?
    记一次全站代理切换----血的教训
    tomcat十大安全优化措施
    paramiko模块使用
    日志分析 第七章 安装grafana
    日志分析 第六章 安装elasticsearch
    日志分析 第五章 安装logstash
    日志分析 第四章 安装filebeat
    IO多路复用及ThreadingTCPServer源码阅读
    socket编程--socket模块介绍
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14382785.html
Copyright © 2020-2023  润新知