题意: 一个数字矩阵,可以出发K次,每次可以从右边或者下面走,要求(在收益最大情况下)覆盖全图,不能则输出-1。(规则:每次跳一步的时候若格子数字相等则获得该数字的能量,每跳一步消耗距离的能量)。每个格子走且仅能走一次。

选<=K条路径,最优情况来覆盖全图。

显然用拆点为二分图。

一种解法:边(流量,费用)

源点向X部连边(1,0)Y部向汇点连边(1,0)X到Y,若能到,则有边(1,消耗-获得)。关键点(解决每个点都覆盖,恰好起到填补的作用):在X部最上面添加一个点,源点连之(k,0)它向所有Y点连边(1,0)。跑最小费用最大流即可。

第二种:(感想zz1215提供的建图思路)

源点向X部连边(1,0)Y部向汇点连边(1,0),Y到X,若能到,则有边(1,消耗-获得)(注意这里是回流),每个点I-->I`有边(1,-w_inf),这里的w_inf为相对大数,只要保证该费用较“小”即可(相对其他费用,他是最廉价的,这样必优先流这条边。添加超级源点,向源点连边(K,0)。增广K次中,若一直增大,则取最大,否则到开始下降的时候要BREAK。(先曾后减的)。

PS:开始时候因为定位编号搞错有没有!编号(i,j)=i*m+j,而不是i*n+j!!!

图:

代码:

#include<iostream>              //24ms
#include<cstdio>
#include<algorithm>
#include<queue>
#include<string>
using namespace std;
int n,m,k;
const int inf=0x3f3f3f3f;
int a[25][25];
int head[500];int e[10000][4];int nume=0;
void inline adde(int i,int j,int c,int w)
{
e[nume][0]=j;e[nume][1]=head[i];head[i]=nume;
e[nume][2]=c;e[nume++][3]=w;
e[nume][0]=i;e[nume][1]=head[j];head[j]=nume;
e[nume][2]=0;e[nume++][3]=-w;
}
int inq[500];int d[500];
bool spfa(int &sumcost)
{
for(int i=0;i<=2*n*m+3;i++)
{
inq[i]=0;d[i]=inf;
}
int minf=inf;
queue<int>q;
int prv[300];int pre[300];
q.push(2*n*m+2);
inq[2*n*m+2]=1;
d[2*n*m+2]=0;
while(!q.empty())
{
int cur=q.front();
q.pop();inq[cur]=0;
for(int i=head[cur];i!=-1;i=e[i][1])
{
int v=e[i][0];
if(e[i][2]>0&&d[v]>e[i][3]+d[cur])
{
d[v]=e[i][3]+d[cur];
prv[v]=cur;
pre[v]=i;
if(!inq[v])
{
q.push(v);
inq[v]=1;
}
}
}
}
if(d[2*n*m+1]==inf)return 0;
int cur=2*n*m+1;
while(cur!=2*n*m+2)
{
minf=min(minf,e[pre[cur]][2]);
cur=prv[cur];
}
cur=2*n*m+1;
while(cur!=2*n*m+2)
{
e[pre[cur]][2]-=minf;e[pre[cur]^1][2]+=minf;
cur=prv[cur];
}
sumcost+=d[2*n*m+1]*minf;
return 1;
}
int mincost()
{
int sum=0;
while(spfa(sum));
return sum;
}
void init()
{
nume=0;
for(int i=0;i<=2*n*m+3;i++)
{
head[i]=-1;
}
}
int main()
{
int T;
scanf("%d",&T);
for(int iii=1;iii<=T;iii++)
{
scanf("%d%d%d",&n,&m,&k);
init();
string s;
for(int i=0;i<n;i++)
{
cin>>s;
for(int j=0;j<m;j++)
{
a[i][j]=s[j]-'0';
}
}
printf("Case %d : ",iii); if(min(n,m)>k)
{
printf("-1\n");continue;
}
for(int i=0;i<n;i++) //起点2*n*m+2,终点2*n*m+1
for(int j=0;j<m;j++)
{
for(int ii=i+1;ii<n;ii++)
{
int temp=(a[i][j]==a[ii][j]?a[i][j]:0);
adde(m*i+j,m*ii+j+n*m+1,1,ii-i-1-temp);
} for(int jj=j+1;jj<m;jj++)
{
int temp=(a[i][j]==a[i][jj]?a[i][j]:0);
adde(m*i+j,m*i+jj+n*m+1,1,jj-j-1-temp);
}
}
for(int i=0;i<n*m;i++)
adde(2*n*m+2,i,1,0);
adde(2*n*m+2,n*m,k,0);
for(int i=n*m+1;i<=2*n*m;i++)
{
adde(n*m,i,1,0);
adde(i,2*n*m+1,1,0);
}
/* for(int i=0;i<=2*n*m+2;i++)
for(int j=head[i];j!=-1;j=e[j][1])
printf("%d->%d:c:%d,w:%d\n",i,e[j][0],e[j][2],e[j][3]);*/
printf("%d\n",-mincost());
}
return 0;
}

方法二:

#include<iostream>           //31ms
#include<cstdio>
#include<queue>
#include<string>
using namespace std;
int n,m,k;
const int inf=0x3f3f3f3f;
const int winf=100000;
int a[25][25];
int head[500];int e[20001][4];int nume=0;
void inline adde(int i,int j,int c,int w)
{
e[nume][0]=j;e[nume][1]=head[i];head[i]=nume;
e[nume][2]=c;e[nume++][3]=w;
e[nume][0]=i;e[nume][1]=head[j];head[j]=nume;
e[nume][2]=0;e[nume++][3]=-w;
}
int inq[500];int d[500];
bool spfa(long long &sumcost)
{
for(int i=0;i<=2*n*m+3;i++)
{
inq[i]=0;d[i]=inf;
}
int prv[500];int pre[500];
int minf=inf;
queue<int>q;
q.push(n*m);
inq[n*m]=1;
d[n*m]=0;
while(!q.empty())
{
int cur=q.front();
q.pop();
inq[cur]=0;
for(int i=head[cur];i!=-1;i=e[i][1])
{
int v=e[i][0];
if(e[i][2]>0&&d[v]>e[i][3]+d[cur])
{
d[v]=e[i][3]+d[cur];
prv[v]=cur;
pre[v]=i;
if(!inq[v])
{
q.push(v);
inq[v]=1;
}
}
}
}
if(d[2*n*m+1]==inf)return 0;
int cur=2*n*m+1;
while(cur!=n*m)
{
minf=min(minf,e[pre[cur]][2]);
cur=prv[cur];
}
cur=2*n*m+1;
while(cur!=n*m)
{
e[pre[cur]][2]-=minf;
e[pre[cur]^1][2]+=minf;
cur=prv[cur];
}
sumcost+=d[2*n*m+1]*(long long)minf;
return 1;
}
long long mincost()
{
long long sum=0;
long long lastsum=0;
while(spfa(sum)) //变小的时候跳出
{
if(lastsum>=-sum){return -lastsum;}
lastsum=-sum;
}
return sum;
}
void init()
{
nume=0;
for(int i=0;i<=2*n*m+3;i++)
{
head[i]=-1;
}
}
int main()
{
int T;
scanf("%d",&T);
for(int iii=1;iii<=T;iii++)
{
scanf("%d%d%d",&n,&m,&k);
init();
string s;
for(int i=0;i<n;i++)
{
cin>>s;
for(int j=0;j<m;j++)
{
a[i][j]=s[j]-'0';
}
}
printf("Case %d : ",iii); if(min(n,m)>k)
{
printf("-1\n");continue;
}
for(int i=0;i<n;i++) //起点n*m,终点:2*n*m+1
for(int j=0;j<m;j++)
{
for(int ii=i+1;ii<n;ii++)
{
int temp=(a[i][j]==a[ii][j]?a[i][j]:0);
adde(m*i+j+n*m+1,m*ii+j,1,ii-i-1-temp);
}
for(int jj=j+1;jj<m;jj++)
{
int temp=(a[i][j]==a[i][jj]?a[i][j]:0);
adde(m*i+j+n*m+1,m*i+jj,1,jj-j-1-temp);
}
}
for(int i=0;i<n*m;i++)
adde(2*n*m+2,i,1,0);
adde(n*m,n*m*2+2,k,0);
for(int i=n*m+1;i<=2*n*m;i++)
{
adde(i-n*m-1,i,1,-winf);
adde(i,2*n*m+1,1,0);
}
/* for(int i=0;i<=2*n*m+2;i++)
for(int j=head[i];j!=-1;j=e[j][1])
printf("%d->%d:c:%d,w:%d\n",i,e[j][0],e[j][2],e[j][3]);*/
cout<<-mincost()-n*m*winf<<endl;
}
return 0;
}

hdu4862 2014多校B题/ 费用流(最优情况下用不大于K条路径覆盖)(不同的解法)的更多相关文章

  1. 【思维题 费用流 技巧】bzoj5403: marshland

    主要还是网络流拆点建图一类技巧吧 Description JudgeOnline/upload/201806/1(4).pdf 题目分析 第一眼看到这题时候只会把每个点拆成4个方向:再强制定向连边防止 ...

  2. 补 第三场多校杭电 费用流 K Subsequence

    K Subsequence 这个题目是这个人想吃东西,但是他每次吃的都是他的美味值都必须不递减,可以吃k次,问这个最大的美味值是多少. 这个是一个比较明显的费用流,建图也很好建,但是呢,这个题目卡sp ...

  3. CFGYM 2013-2014 CT S01E03 D题 费用流模版题

    题意: n行, a房间的气球,b房间的气球 i行需要的气球,与a房的距离,b房的距离 求最小距离 #include <stdio.h> #include <string.h> ...

  4. BZOJ.2324.[ZJOI2011]营救皮卡丘(费用流 Floyd)

    BZOJ 洛谷 首先预处理出\(dis[i][j]\),表示从\(i\)到\(j\)的最短路.可以用\(Floyd\)处理. 注意\(i,j\)是没有大小关系限制的(\(i>j\)的\(dis[ ...

  5. BZOJ2324 ZJOI2011营救皮卡丘(floyd+上下界费用流)

    虽然不一定每次都是由编号小的点向编号大的走,但一个人摧毁的顺序一定是从编号小的到编号大的.那么在摧毁据点x的过程中,其只能经过编号小于x的点.并且这样一定合法,因为可以控制其他人先去摧毁所经过的点.那 ...

  6. hdu4862 费用流(不错)

    题意:       给你一个矩阵,你最多可以选择k条路线,k条路线的起点随意,每次行走的距离随意,但是只能往右或者下走,走过的点不能再走,而且每一步如果a->b,如果a和b的权值s相等那么就可以 ...

  7. 【BZOJ3130】费用流(最大流,二分)

    [BZOJ3130]费用流(最大流,二分) 题面 Description Alice和Bob在图论课程上学习了最大流和最小费用最大流的相关知识. 最大流问题:给定一张有向图表示运输网络,一个源点S和一 ...

  8. 【BZOJ2324】[ZJOI2011]营救皮卡丘(网络流,费用流)

    [BZOJ2324][ZJOI2011]营救皮卡丘(网络流,费用流) 题面 BZOJ 洛谷 题解 如果考虑每个人走的路径,就会很麻烦. 转过来考虑每个人破坏的点集,这样子每个人可以得到一个上升的序列. ...

  9. [费用流][BZOJ1070]修车

    修车 题目描述 同一时刻有位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的.现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均 ...

随机推荐

  1. Base64编码密钥时关于换行的几个问题。

    在windows下一个javaweb应用,需要用http传递公钥pk.一般是String pk = BASE64ENCODER.encode(pkBytes);base64编码时,每76个字母就要换行 ...

  2. Spring启动流程—源码解读

    https://blog.csdn.net/yangliuhbhd/article/details/80790761 Spring的AbstractApplicationContext的refresh ...

  3. go get 升级所有

    go get -u all go get -u go mod update go get -u full_package_name    go get -u github.com/... // ('. ...

  4. Python基础篇 -- 小数据池和再谈编码

    小数据池 1. id() 通过id()可以查看到一个变量表示的值在内存中的地址 s = "Agoni" print(id(s)) # 2410961093272 2. is 和 = ...

  5. null 理解

    值 null 特指对象的值未设置.它是 JavaScript 基本类型 之一. 语法节 null 描述节 值 null 是一个字面量,它不像undefined 是全局对象的一个属性.null 是表示缺 ...

  6. angstromctf -No libc for You

    0x00 syscall syscall函数原型为: int syscall(int number, ...) 其中number是系统调用号,number后面应顺序接上该系统调用的所有参数.大概意思是 ...

  7. web安全--<a>标签带有target=“_blank”

    面试时遇到安全相关的一个题目 :超链接<a>标签带有target=“_blank”属性的,容易被利用进行诸如钓鱼等攻击,请问如何在书写代码时进行防范?(谷歌和火狐环境). 自己看到这道题目 ...

  8. [LUOGU] P2593 [ZJOI2006]超级麻将

    f[a][b][c][i]表示考虑到第i个,第i位用了b个,第i-1位用了a个,此时有将/无将(c=1/0)的情况是否可达. 转移分以下几类: 1.调一个将 f[a][b][1][i]|=f[a][b ...

  9. PHP开发中涉及到emoji表情的几种处理方法!

    emoji表情 处理 一般Mysql表设计时,都是用UTF8字符集的.把带有emoji的昵称字段往里面insert一下就没了,整个字段变成了空字符串.这是怎么回事呢? 原来是因为Mysql的utf8字 ...

  10. PHP发送邮件标题乱码的解决

    遇到问题:PHPMailer发送邮件时中文乱码,本来我的系统都是英文内容的,后来需求变化需要在标题中添加中文,但是在使用安卓自带邮件工具收取是出现乱码,而使用QQ邮箱查看确实正常的. 解决方法: 先用 ...