【BZOJ 3232】圈地游戏

题目链接

题目描述

DZY家的后院有一块地,由N行M列的方格组成,格子内种的菜有一定的价值,并且每一条单位长度的格线有一定的费用。
DZY喜欢在地里散步。他总是从任意一个格点出发,沿着格线行走直到回到出发点,且在行走途中不允许与已走过的路线有任何相交或触碰(出发点除外)。记这条封闭路线内部的格子总价值为V,路线上的费用总和为C,DZY想知道V/C的最大值是多少。

Sol

分数规划

一个封闭图形可以在走的时候把边权该为 原边权 减去(加上) 这一列格子价值的前缀和
可以规定向左走为正 , 向右走为负

然后就是 边权取反+判负圈 了 (这个是真的难搞TAT)

堆栈版SPFA , 不要求最短路不要设 dis 为INF !!! 直接都相同即可

因为一个负圈必然可以找到一种走法,使得边权一直为负(似乎是这样) !!!

#include
#include
#include
#include
#include
#include
#include
#include
#define Set(a,b) memset(a,b,sizeof(a))
using namespace std;
inline int read()
{
	int x=0;char ch=getchar();int t=1;
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
	return x*t;
}
typedef long long ll;
typedef double db;
const int N=52;
const int MAXN=N*N;
const db INF=1e9;
const db eps=1e-5;
db V[MAXN];
int n,m;
struct edge{
	int to,next;int len;db w;int id;bool flag;
}a[8*MAXN];
int head[MAXN];int cnt=0;
int S,T;
bool in[MAXN];
db dis[MAXN];
inline void add(int x,int y,int w,bool fl){a[++cnt]=(edge){y,head[x],w,0.0,0,fl};head[x]=cnt;}
bool dfs(int u)
{
	if(in[u]) return 1;
	in[u]=1;
	for(register int v,i=head[u];i;i=a[i].next){
		v=a[i].to;
		if(dis[v]>dis[u]+a[i].w){
			dis[v]=dis[u]+a[i].w;
			bool g=dfs(v);
			if(g) return 1;
		}
	}
	in[u]=0;
	return 0;
}
inline bool check(db X)
{
	for(register int i=1;i<=cnt;++i){
		if(a[i].flag==1) a[i].w=V[a[i].id]-(db)a[i].len*X;
		else a[i].w=-V[a[i].id]-(db)(a[i].len*X);
		a[i].w=-a[i].w;
	}
	Set(dis,0);Set(in,0);
	for(register int i=1;i<=T;++i) if(dfs(i)) return 1;
	return 0;
}
int main()
{
	n=read();m=read();int h=0;T=(n+1)*(m+1);S=0;
	for(register int i=1;i<=n;++i) for(register int j=1;j<=m;++j) V[++h]=read();
	h=m;
	for(register int i=2;i<=n;++i) for(register int j=1;j<=m;++j) V[++h]+=V[h-m];
	h=0;V[0]=0.0;
	for(register int i=1;i<=n+1;++i)
		for(register int j=1;j<=m;++j){
			++h;register int w=read();
			add(h,h+1,w,1);a[cnt].id=max(0,h-m-i+1);
			add(h+1,h,w,0);a[cnt].id=max(0,h-m-i+1);
			if(j==m) ++h;
		}
	h=0;
	for(register int i=1;i<=n;++i){
		for(register int j=1;j<=m+1;++j){
			++h;register int w=read();
			add(h,h+m+1,w,0);add(h+m+1,h,w,0);
		}
	}
	register db l=1.0,r=250000.00,ans=0.0;
	while(r-l>=eps){
		register db mid=(l+r)/2.000;
		if(check(mid)) l=mid,ans=mid;
		else r=mid;
	}
	printf("%.3lf\n",ans);
}

你可能感兴趣的:(======题解======,——分治——,分数规划)