【2011集训队出题】【GDKOI2010】圈地计划

10 篇文章 0 订阅
4 篇文章 0 订阅

二元关系
这是两道题,大体相同,不过有一点点不同
我在题目描述中称为题1和题2

Description

最近房地产商GDOI(Group of Dumbbells Or Idiots)从NOI(Nuts Old Idiots)手中得到了一块开发土地。据了解,这块土地是一块矩形的区域,可以纵横划分为N×M块小区域。GDOI要求将这些区域分为商业区和工业区来开发。根据不同的地形环境,每块小区域建造商业区和工业区能取得不同的经济价值。更具体点,对于第i行第j列的区域,建造商业区将得到Aij收益,建造工业区将得到Bij收益。

题1
另外同种的区域连在一起可以得到额外的收益,即如果相邻有K块(显然K不超过4)同种类型的区域,则这块区域能增加k×Cij收益。

上面注意是相同的区域可获得收益,而下面的是不同的区域可获得收益

题2
另外不同的区域连在一起可以得到额外的收益,即如果区域(I,j)相邻(相邻是指两个格子有公共边)有K块(显然K不超过4)类型不同于(I,j)的区域,则这块区域能增加k×Cij收益。

经过Tiger.S教授的勘察,收益矩阵A,B,C都已经知道了。你能帮GDOI求出一个收益最大的方案么?

Input

输入第一行为两个整数,分别为正整数N和M,分别表示区域的行数和列数。
第2到N+1列,每行M个整数,表示商业区收益矩阵A。
第N+2到2N+1列,每行M个整数,表示工业区收益矩阵B。
第2N+2到3N+1行,每行M个整数,表示相邻额外收益矩阵C。
Output
输出只有一行,包含一个整数,为最大收益值。

Sample Input

3 3
1 2 3
4 5 6
7 8 9
9 8 7
6 5 4
3 2 1
1 1 1
1 3 1
1 1 1

Sample Output

题1:87
题2:81

Solution

这就是二元关系的模板题
有大神写过详细的解释,这里就不会太详细了
我们求的是最小割,那么对于求最小的喜悦值就可以直接用
而求最大的呢?取相反数?然而网络流不能负流
所以首先对于答案增加所有的喜悦值,这样用总喜悦值减去最小割就是最大喜悦值
中间过程自己推

这里直接给连边方法和结果
对于题1(即相同的有额外收益)
则源点向所有点连商业区的收益,所有点向汇点连工业区的收益,相邻的点连双向边额外的收益
答案就是总收益减去最小割

对于题2(即不同的有额外收益)
则黑白染色,相邻(有关系)的点染不同的颜色
源点向所有白点连商业区的收益,所有白点向汇点连工业区的收益
源点向所有黑点连工业区的收益,所有黑点向汇点连商业区的收益
相邻的点连双向边额外的收益
答案就是总收益减去最小割

几乎一样!

Code题1

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 10100
#define no(i,j) (((i)-1)*m+(j))
#define INF 2147483647
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,s,t,next[N*20],to[N*20],last[N*20],data[N*20],tot=1,ddd[4]={0,1},dddd[4]={1,0},a[110][110];
int d[N*20],bz[N],ans;
void putin(int x,int y,int z,int z1)
{
    next[++tot]=last[x];last[x]=tot;to[tot]=y;data[tot]=z;
    next[++tot]=last[y];last[y]=tot;to[tot]=x;data[tot]=z1;
}
bool bfs()
{
    int i=0,j=1;d[1]=s;memset(bz,0,sizeof(bz));bz[s]=1;
    while(i<j)
    {
        int l=d[++i];
        for(int k=last[l];k;k=next[k])
            if(data[k]>0&&bz[to[k]]==0) bz[to[k]]=bz[l]+1,d[++j]=to[k];
    }
    return bz[t]!=0;
}
int dfs(int x,int v)
{
    if(x==t) return v;int ans=0;
    for(int i=last[x];i;i=next[i])
    {
        int y=to[i];
        if(bz[y]==bz[x]+1&&data[i]>0)
        {
            int jy=dfs(y,min(v,data[i]));
            if(jy) data[i]-=jy,data[i^1]+=jy,ans+=jy,v-=jy;
            if(v==0) return ans;
        }
    }
    if(ans==0) bz[x]=-1;
    return ans;
}
int main()
{
    freopen("1144.in","r",stdin);
    scanf("%d%d",&n,&m);s=n*m+1;t=n*m+2;
    fo(i,1,n)
        fo(j,1,m)
        {
            int x;scanf("%d",&x);
            putin(s,no(i,j),x,0);ans+=x;
        }
    fo(i,1,n)
        fo(j,1,m)
        {
            int x;scanf("%d",&x);
            putin(no(i,j),t,x,0);ans+=x;
        }
    fo(i,1,n) fo(j,1,n) scanf("%d",&a[i][j]);
    fo(i,1,n)
        fo(j,1,m)
        {
            fo(k,0,1)
            {
                int x=i+ddd[k],y=j+dddd[k];
                if(x>0&&y>0&&x<=n&&y<=m) putin(no(i,j),no(x,y),a[i][j]+a[x][y],a[i][j]+a[x][y]),ans+=(a[i][j]+a[x][y]);
            }
        }
    while(bfs()) ans-=dfs(s,INF);
    printf("%d",ans);
}

Code题2

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 10100
#define no(i,j) (((i)-1)*m+(j))
#define INF 214748367
using namespace std;
int n,m,s,t,next[N*10],to[N*10],last[N*10],data[N*10],tot=1,ddd[4]={0,1,0,-1},dddd[4]={1,0,-1,0},a[101][101];
int d[N*10],bz[N];
double ans=0;
void putin(int x,int y,int z,int z1)
{
    next[++tot]=last[x];last[x]=tot;to[tot]=y;data[tot]=z;
    next[++tot]=last[y];last[y]=tot;to[tot]=x;data[tot]=z1;
}
bool bfs()
{
    memset(bz,0,sizeof(bz));int i=0,j=1;d[1]=s;bz[s]=1;
    while(i<j)
    {
        int k=d[++i];
        for(int l=last[k];l;l=next[l])
        {
            int y=to[l];
            if(bz[y]==0&&data[l]>0) bz[y]=bz[k]+1,d[++j]=y;
        }
    }
    return bz[t];
}
int dinic(int x,int v)
{
    if(x==t) return v;int ans=0;
    for(int i=last[x];i;i=next[i])
    {
        int y=to[i],qq=0;
        if(bz[y]==bz[x]+1) qq=dinic(y,min(v,data[i]));
        if(qq) v-=qq,data[i]-=qq,data[i^1]+=qq,ans+=qq;
        if(v==0) return ans;
    }
    if(ans==0) bz[x]=-1;
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);s=2*n*m+1;t=2*n*m+2;
    fo(i,1,n)
        fo(j,1,m)
        {
            int x;scanf("%d",&x);ans+=x;
            if((i+j)%2==0) putin(s,no(i,j),x,0);else putin(no(i,j),t,x,0);//黑白染色
        }
    fo(i,1,n)
        fo(j,1,m)
        {
            int x;scanf("%d",&x);ans+=x;
            if((i+j)%2==0) putin(no(i,j),t,x,0);else putin(s,no(i,j),x,0);//黑白染色
        }
    fo(i,1,n) fo(j,1,m) scanf("%d",&a[i][j]);
    fo(i,1,n)
        fo(j,1,m)
        {
            fo(k,0,1)
            {
                int x=i+ddd[k],y=j+dddd[k];
                if(x>0&&y>0&&x<=n&&y<=m) putin(no(i,j),no(x,y),a[i][j]+a[x][y],a[i][j]+a[x][y]),ans+=(a[i][j]+a[x][y]);
            }
        }
    while(bfs()) ans-=dinic(s,INF);
    printf("%.0lf",ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值