HDU 3820 Golden Eggs (SAP | Dinic)

本文详细解析了GoldenEggs游戏中的算法问题,旨在最大化玩家收益。通过构建二分图最大点权独立集,结合hdu3657:Game的策略,巧妙处理相邻相同颜色蛋的收益扣除,利用最小割算法求解最大收益。

Golden Eggs

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 304    Accepted Submission(s): 172


Problem Description
There is a grid with N rows and M columns. In each cell you can choose to put a golden or silver egg in it, or just leave it empty. If you put an egg in the cell, you will get some points which depends on the color of the egg. But for every pair of adjacent eggs with the same color, you lose G points if there are golden and lose S points otherwise. Two eggs are adjacent if and only if there are in the two cells which share an edge. Try to make your points as high as possible.
 

 

Input
The first line contains an integer T indicating the number of test cases.
There are four integers N, M, G and S in the first line of each test case. Then 2*N lines follows, each line contains M integers. The j-th integer of the i-th line Aij indicates the points you will get if there is a golden egg in the cell(i,j). The j-th integer of the (i+N)-th line Bij indicates the points you will get if there is a silver egg in the cell(i,j).

Technical Specification
1. 1 <= T <= 20
2. 1 <= N,M <= 50
3. 1 <= G,S <= 10000
4. 1 <= Aij,Bij <= 10000
 

 

Output
For each test case, output the case number first and then output the highest points in a line.
 

 

Sample Input
2 2 2 100 100 1 1 5 1 1 4 1 1 1 4 85 95 100 100 10 10 10 10 100 100
 

 

Sample Output
Case 1: 9 Case 2: 225
 

 

Author
hanshuai
 

 

Source
 

 

Recommend
lcy
 

 还是SAP快:

大致题意:
    给出一块n*m的区域,已知区域中的每个格子都可以放金蛋或者是银弹,已知每个格子放金蛋或者是放银弹时可以得到的收益,分别用矩阵map1[][],map2[][]表示。如果相邻的两个格子放的蛋相同的话会扣去一定的收益,金蛋的话会扣去g,银弹s。求收益的最大值是多少。
 
大致思路:
    首先,对于某个确定的位置,只能选择金蛋或者银弹的其中一个放上去,从这里可以想到和二分图最大点权独立集有关。其次,如果相邻的位置放的蛋相同的话需要付出一定的花费这个又和hdu 3657:Game有些许相似之处。所以我们这么构图:
 
按照黑白染色把矩阵中的位置分为A,B两个集合,每个集合中的位置拆为k,k'两个点。对于A集合,从源点向k连边,容量为它放金蛋是的收获,从k再向k'连边容量设为inf,从k'连到汇点,容量为其放银弹时的收益。这样设的目的就是为了使得割必须在源点到k或者k'到汇点上。   
对于B集合,我们做一些改变,把从源点到k的容量设为放银弹的收益,k'到汇点的容量设为其放金蛋的收益,k和k'之间容量为inf的边不变。至于为什么看下一步。
然后再接下来一步加边就是关键了!我们知道如果A集合中的金蛋和B集合中的金蛋相邻的话会付出一定的话费,为了表示设A集合中的某位置a是金蛋,我们会连接源点->a。为了表示B集合中位置b是金蛋的,我们会连接b'->汇点。好的,在这里我们假设b位置和a相邻。为了表示a位置和b位置同时放金蛋需要G的花费,我们连接a->b'并把其容量设为G,对于银弹我们做同样的处理。至于为什么,好好把hdu3567刷掉就全明白了
接下来,用所有的map1[i][j]+map2[i][j]-最小割得到的就是答案。
 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>

using namespace std;

const int VM=101000;
const int EM=500100;
const int INF=0x3f3f3f3f;

struct Edge{
    int to,nxt;
    int cap;
}edge[EM<<1];

int n,m,G,S,cnt,head[VM],src,des,map1[110][110],map2[110][110];
int dep[VM],gap[VM],cur[VM],aug[VM],pre[VM];

void addedge(int cu,int cv,int cw){
    edge[cnt].to=cv;  edge[cnt].cap=cw;  edge[cnt].nxt=head[cu];
    head[cu]=cnt++;
    edge[cnt].to=cu;  edge[cnt].cap=0;   edge[cnt].nxt=head[cv];
    head[cv]=cnt++;
}

int SAP(int n){
    int max_flow=0,u=src,v;
    int id,mindep;
    aug[src]=INF;
    pre[src]=-1;
    memset(dep,0,sizeof(dep));
    memset(gap,0,sizeof(gap));
    gap[0]=n;
    for(int i=0;i<=n;i++)
        cur[i]=head[i]; // 初始化当前弧为第一条弧
    while(dep[src]<n){
        int flag=0;
        if(u==des){
            max_flow+=aug[des];
            for(v=pre[des];v!=-1;v=pre[v]){     // 路径回溯更新残留网络
                id=cur[v];
                edge[id].cap-=aug[des];
                edge[id^1].cap+=aug[des];
                aug[v]-=aug[des];   // 修改可增广量,以后会用到
                if(edge[id].cap==0) // 不回退到源点,仅回退到容量为0的弧的弧尾
                    u=v;
            }
        }
        for(int i=cur[u];i!=-1;i=edge[i].nxt){
            v=edge[i].to;    // 从当前弧开始查找允许弧
            if(edge[i].cap>0 && dep[u]==dep[v]+1){  // 找到允许弧
                flag=1;
                pre[v]=u;
                cur[u]=i;
                aug[v]=min(aug[u],edge[i].cap);
                u=v;
                break;
            }
        }
        if(!flag){
            if(--gap[dep[u]]==0)    // gap优化,层次树出现断层则结束算法
                break;
            mindep=n;
            cur[u]=head[u];
            for(int i=head[u];i!=-1;i=edge[i].nxt){
                v=edge[i].to;
                if(edge[i].cap>0 && dep[v]<mindep){
                    mindep=dep[v];
                    cur[u]=i;   // 修改标号的同时修改当前弧
                }
            }
            dep[u]=mindep+1;
            gap[dep[u]]++;
            if(u!=src)  // 回溯继续寻找允许弧
                u=pre[u];
        }
    }
    return max_flow;
}

int main(){

    //freopen("input.txt","r",stdin);

    int t,cases=0;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d%d",&n,&m,&G,&S);
        cnt=0;
        memset(head,-1,sizeof(head));
        src=0; des=n*m*2+1;
        int sum=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                scanf("%d",&map1[i][j]);
                sum+=map1[i][j];
            }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                scanf("%d",&map2[i][j]);
                sum+=map2[i][j];
            }
        int tmp;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                tmp=(i-1)*m+j;
                if((i+j)%2==0){
                    addedge(src,tmp,map1[i][j]);
                    addedge(tmp,tmp+n*m,INF);
                    addedge(tmp+n*m,des,map2[i][j]);

                    if(i>1) addedge(tmp,tmp-m+n*m,G);
                    if(i<n) addedge(tmp,tmp+m+n*m,G);
                    if(j>1) addedge(tmp,tmp-1+n*m,G);
                    if(j<m) addedge(tmp,tmp+1+n*m,G);

                }else{
                    addedge(src,tmp,map2[i][j]);
                    addedge(tmp,tmp+n*m,INF);
                    addedge(tmp+n*m,des,map1[i][j]);

                    if(i>1) addedge(tmp,tmp-m+n*m,S);
                    if(i<n) addedge(tmp,tmp+m+n*m,S);
                    if(j>1) addedge(tmp,tmp-1+n*m,S);
                    if(j<m) addedge(tmp,tmp+1+n*m,S);
                }
            }
        }
        printf("Case %d: %d\n",++cases,sum-SAP(des+1));
    }
    return 0;
}

 

 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>

using namespace std;

const int VM=101000;
const int EM=500100;
const int INF=0x3f3f3f3f;

struct Edge{
    int to,nxt;
    int cap;
}edge[EM<<1];

int n,m,G,S,cnt,head[VM],src,des;
int map1[110][110],map2[110][110],dep[VM];    //dep[i]表示当前点到起点src的层数

void addedge(int cu,int cv,int cw){
    edge[cnt].to=cv;  edge[cnt].cap=cw;  edge[cnt].nxt=head[cu];
    head[cu]=cnt++;
    edge[cnt].to=cu;  edge[cnt].cap=0;   edge[cnt].nxt=head[cv];
    head[cv]=cnt++;
}

int BFS(){      // 重新建图(按层数建图)
    queue<int> q;
    while(!q.empty())
        q.pop();
    memset(dep,-1,sizeof(dep));
    dep[src]=0;
    q.push(src);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=head[u];i!=-1;i=edge[i].nxt){
            int v=edge[i].to;
            if(edge[i].cap>0 && dep[v]==-1){      // 如果可以到达且还没有访问
                dep[v]=dep[u]+1;
                q.push(v);
            }
        }
    }
    return dep[des]!=-1;
}

int DFS(int u,int minx){    // 查找路径上的最小的流量
    if(u==des)
        return minx;
    int tmp;
    for(int i=head[u];i!=-1;i=edge[i].nxt){
        int v=edge[i].to;
        if(edge[i].cap>0 && dep[v]==dep[u]+1 && (tmp=DFS(v,min(minx,edge[i].cap)))){
            edge[i].cap-=tmp;     //正向减少
            edge[i^1].cap+=tmp;     //反向增加
            return tmp;
        }
    }
    dep[u]=-1;  //这一句作用无穷大,不在TLE。。。。。。。。。。。
    return 0;
}

int Dinic(){
    int ans=0,tmp;
    while(BFS()){
        while(1){
            tmp=DFS(src,INF);
            if(tmp==0)
                break;
            ans+=tmp;
        }
    }
    return ans;
}

int main(){

    //freopen("input.txt","r",stdin);

    int t,cases=0;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d%d",&n,&m,&G,&S);
        cnt=0;
        memset(head,-1,sizeof(head));
        src=0; des=n*m*2+1;
        int sum=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                scanf("%d",&map1[i][j]);
                sum+=map1[i][j];
            }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                scanf("%d",&map2[i][j]);
                sum+=map2[i][j];
            }
        int tmp;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                tmp=(i-1)*m+j;
                if((i+j)%2==0){
                    addedge(src,tmp,map1[i][j]);
                    addedge(tmp,tmp+n*m,INF);
                    addedge(tmp+n*m,des,map2[i][j]);
                    //
                    if(i!=1)addedge(tmp,tmp-m+n*m,G);
                    if(i!=n)addedge(tmp,tmp+m+n*m,G);
                    if(j!=1)addedge(tmp,tmp-1+n*m,G);
                    if(j!=m)addedge(tmp,tmp+1+n*m,G);
                    //
                }
                else{
                    addedge(src,tmp,map2[i][j]);
                    addedge(tmp,tmp+n*m,INF);
                    addedge(tmp+n*m,des,map1[i][j]);
                    if(i!=1)addedge(tmp,tmp-m+n*m,S);
                    if(i!=n)addedge(tmp,tmp+m+n*m,S);
                    if(j!=1)addedge(tmp,tmp-1+n*m,S);
                    if(j!=m)addedge(tmp,tmp+1+n*m,S);
                }
            }
        }
        printf("Case %d: %d\n",++cases,sum-Dinic());
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/jackge/p/3239762.html

内容概要:本文系统梳理了多个科研领域的前沿研究与技术实现,重点涵盖FDTD方法中的完美匹配层(PML)研究,以及Matlab/Simulink在电磁、电力、控制、通信、信号处理、图像处理、路径规划、能源系统优化等领域的仿真与算法实现。文中列举了大量基于Matlab和Python的科研案例,如风电功率预测、负荷预测、无人机三维路径规划、电池系统故障诊断、雷达模拟、通信编码、微电网优化调度等,并强调结合智能优化算法(如粒子群、遗传算法、深度学习等)提升系统性能。同时,提供了丰富的代码资源与仿真模型,涵盖永磁同步电机控制、逆变器设计、多智能体任务分配、虚拟电厂调度等复杂系统,助力科研人员快速开展复现实验与创新研究。; 适合人群:具备一定编程基础,熟悉Matlab/Python工具,从事电气工程、自动化、通信、人工智能、新能源、控制科学等相关领域研究的研发人员及研究生。; 使用场景及目标:① 学习并实现FDTD仿真中的PML边界条件以有效抑制数值反射;② 掌握Matlab/Simulink在多物理场建模、控制系统设计与优化算法中的综合应用;③ 借助提供的代码资源完成科研复现、课程设计、竞赛项目或工程原型开发; 阅读建议:此资源以科研实战为导向,不仅提供理论方法,更强调代码实现与仿真验证。建议读者结合自身研究方向,按目录顺序查阅相关模块,下载配套代码进行调试与二次开发,以达到学以致用、融会贯通的目的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值