hdu4975 A simple Gaussian elimination problem.(正确解法 最大流+删边判环)(Updated 2014-10-16)
这题标程是错的,网上很多题解也是错的。
http://acm.hdu.edu.cn/showproblem.php?pid=4975
2014 Multi-University Training Contest 10
A simple Gaussian elimination problem.Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Problem Description
Dragon is studying math. One day, he drew a table with several rows
and columns, randomly wrote numbers on each elements of the table. Then he counted the sum of each row and column. Since he thought the map will be useless after he got the sums, he destroyed the table after that. However Dragon's mom came back and found what he had done. She would Could you help Dragon to do that? Input
The first line of input contains only one integer, T(<=30), the
number of test cases. Following T blocks, each block describes one test case. There are three lines for each block. The first line The second line contains N integer show the sum of each row. The third line contains M integer show the sum of each column. Output
Each output should occupy one line. Each line should start with "Case
#i: ", with i implying the case number. For each case, if we cannot get the original table, just output: "So naive!", else if we can reconstruct the table by more than one ways, you should output one line contains only: "So young!", otherwise (only one way to reconstruct the table) you should output: "So simple!". Sample Input
3
1 1 5 5 2 2 0 10 0 10 2 2 2 2 2 2 Sample Output
Case #1: So simple!
Case #2: So naive! Case #3: So young! Source
Recommend
|
题意:有n行m列矩阵,给出各行的和、各列的和,矩阵元素需要为0~9,判断无解、唯一解、多解。
题解:网络流+判环。
和hdu4888几乎一模一样,hdu4888题解链接:http://www.cnblogs.com/yuiffy/p/3891639.html
这题的做法就是hdu4888的做法,点上面的链接怒看如何建图,我下面说这题的不同之处。网上很多题解都是错的,标程也是错的。
这题标程有错,但没有反例的数据,所以数据还是对的。但标程使用的错误方法使它运行时间巨短,导致这题时限开得少,直接拿超碉优化的网络流+简单dfs判环也是过不了的。
正确做法应该是用更好的判环方法,我想到的是 dfs判环回溯时删边(不是删点),代码如下:
bool dfs(const int &x,const int &prex) {///深搜判环
int biu=-;
walked[x]=true;
for (int i=head[x]; i!=-; i=e[i].next) {
if(e[i].v==prex){
biu=i;
continue;
}
if (e[i].cap>) {
if(walked[e[i].v]) return true;
if(dfs(e[i].v,x)) return true;
}
if(biu==-) head[x]=e[i].next;///删边,因为这条边为0或者走了这条边却没发现环
else e[biu].next=e[i].next;
biu=i;
}
walked[x]=false;
return false;
}
可以想到,如果选择一条边递归,然后从递归里出来了,说明没找到环,那么走这条边肯定找不到环,以后也不用走了,可以把这条边删了。或者这条边流量为0,也可以删了。
这样的话每条边最多只进一次,O(m)。
其实4888也可以用这种方法,不过4888还要输出结果,所以先记录结果再删。
然后我说一下网上常见的错误解法,因为没有反例数据,这种错误解法可以ac。
错误的dfs判环:
bool walked[maxn];
bool dfs(const int &x,const int &preE) {
int pp=preE^;
for (int &i=head[x]; i!=-; i=e[i].next) {
if(i==(pp))continue;
if (e[i].cap>) {
if(walked[e[i].v]) return true;
walked[e[i].v]=true;
if(dfs(e[i].v,i)) return true;
walked[e[i].v]=false;
}
}
return false;
}
网上很多地方都说这个int &i是个优化,加了这个优化就能过。其实这不是优化,这是乱搞,碰巧没有反例数据。
int &i=head[x]的意思是把i当成head[x]的引用,也就是i其实就是head[x]。
这样,i=e[i].next相当于head[x] = e[head[x]].next,是会改变head[x]的,也就是把上一条出边给扔了!这和我上面说的删边不一样,我上面的是只删递归过的边,而这个会把不能走的边也删掉。这题里不能走的边就是直接回头的那条边。
这样会发生什么情况呢?我来画个图:
1和3、2和3、2和4之间有双向边,而1和4之间只有单向边。当深搜1->3->2->4时,会把4的出边全扔了。本来我们应该找到的环是1->4->2->3->1,可是4的出边已经没了,找不到这个环。
由于大家建图的方法、网络流的方法、dfs的顺序不一致,所以这题不好找到对所有代码都成立的反例输入。如果谁发现好的样例的话希望能写出来,怒艹错误解法。
这个错解有人说是比赛时试出来的;还有人比赛时试验只在m,n<=200时判多解,居然正好也能A…怕了……还是希望以后出题人能认真一点,弄好标程和数据。
再说一下标程的错误解法:标程也是回溯时删东西,不过不是删边是删点……点和你无冤无仇,为什么删它?搜完一个点所有出边可不能保证这个点就没用了…就像我上面给的例子的点4。
代码:
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<set>
#include<stack>
#include<queue>
using namespace std;
#define ll long long
#define usll unsigned ll
#define mz(array) memset(array, 0, sizeof(array))
#define minf(array) memset(array, 0x3f, sizeof(array))
#define REP(i,n) for(i=0;i<(n);i++)
#define FOR(i,x,n) for(i=(x);i<=(n);i++)
#define RD(x) scanf("%d",&x)
#define RD2(x,y) scanf("%d%d",&x,&y)
#define RD3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define WN(x) prllf("%d\n",x);
#define RE freopen("D.in","r",stdin)
#define WE freopen("1biao.out","w",stdout)
#define mp make_pair
#define pb push_back int ans;
int r[],co[];
int k,nr,nc;
int sumr,sumc;
const int maxn=;//点数
const int maxm=;//边数
const int inf=;//MAXINT
struct vnode {
int v,next;
int cap;
};
int cnt,head[maxn];
int h[maxn],g[maxn],d[maxn];//g[i]为标号为i的结点个数,h[i]为i结点的标号,d[]当前弧优化,记录当前弧
bool found;
int n,m,st,ed;//n个点m条边
int augc,flow;//augc为增广路容量,flow为最大流
vnode e[maxm]; inline void add(const int &x,const int &y,const int &z) {
e[cnt].v=y;
e[cnt].cap=z;
e[cnt].next=head[x];
head[x]=cnt;
cnt++; e[cnt].v=x;
e[cnt].cap=;
e[cnt].next=head[y];
head[y]=cnt;
cnt++; } bool walked[maxn];
bool dfs(const int &x,const int &prex) {///深搜判环
int biu=-;
walked[x]=true;
for (int i=head[x]; i!=-; i=e[i].next) {
if(e[i].v==prex)continue;
if (e[i].cap>) {
if(walked[e[i].v]) return true;
if(dfs(e[i].v,x)) return true;
}
if(biu==-) head[x]=e[i].next;///删边,因为这条边为0或者走了这条边却没发现环
else e[biu].next=e[i].next;
biu=i;
}
walked[x]=false;
return false;
} void aug(const int &m) {
int mini,minh=n-;
int augco=augc;
int &i=d[m];///当前弧优化
if (m==ed) { //如果当前结点为汇点
found=true;
flow+=augc; //增加流量
return;
}
for (; i!=-; i=e[i].next) { //寻找容许边
//printf("m=%d,i=%d,e[i].v=%d,e[i].cap=%d,e[i].next=%d\n",m,i,e[i].v,e[i].cap,e[i].next);
//getchar();
if (e[i].cap && h[e[i].v]+==h[m]) { //如果残留容量大于0,如果是容许边
if (e[i].cap < augc) augc=e[i].cap;//如果容许边流量小于当前增广路流量 则更新增广路流量
//d[m]=i; //把i定为当前弧
aug(e[i].v); //递归
if (h[st]>=n) return; //GAP 如果源点距离标号大于n 则停止算法
if (found) break; //如果找到汇点 则退出寻找
augc=augco;//没找到就还原当前的流
}
}
if (!found) { //重标号
for (int i=head[m]; i!=-; i=e[i].next) //找那个标号,这里不能用d[m]开始,不然会蛋疼
if (e[i].cap && h[e[i].v]<minh) {
minh=h[e[i].v];
mini=i;
}
g[h[m]]--; //GAP 距离为
if (!g[h[m]]) h[st]=n; //GAP
h[m]=minh+;
d[m]=mini;
g[h[m]]++; //GAP
} else {
//修改残量
e[i].cap-=augc;
e[i^].cap+=augc;
}
} inline void farm() {
int i,j,x,y,z;
memset(head,-,sizeof(head));
cnt=;
n=nc+nr+;
st=n-;
ed=n;
for(i=; i<=nc; i++)
add(st,i,co[i]);
for(i=; i<=nr; i++)
add(nc+i,ed,r[i]);
for(i=; i<=nc; i++)
for(j=; j<=nr; j++)
add(i,j+nc,k);
memset(h,,sizeof(h));
memset(g,,sizeof(g));
g[]=n;
flow=;
for(i=; i<=n; i++)
d[i]=head[i];//当前弧初始化
while(h[st]<n) {
augc=inf;//初始化增广路容量为正无穷大
found=false;
aug(st);//从源点开始找
}
if(flow!=sumr) {
ans=;
return;
}
memset(walked,false,sizeof(walked));
for(i=; i<=nr; i++) { ///因为建图的特性,有环的话环肯定包含1~nr中的点,也就是表示行的结点
if(dfs(i,-)) {
ans=;
return;
}
}
ans=;
//printf("%d\n",flow);
} inline void read(int &a) {///读入优化
char ch;
bool flag = false;
a = ;
while(!((((ch = getchar()) >= '') && (ch <= '')) || (ch == '-')));
if(ch != '-') {
a *= ;
a += ch - '';
} else {
flag = true;
}
while(((ch = getchar()) >= '') && (ch <= '')) {
a *= ;
a += ch - '';
}
if(flag) {
a = -a;
}
} int main() {
//RE;
//WE;
int i,j,cas=,t;
scanf("%d",&t);
while(t--) {
scanf("%d%d",&nr,&nc);
k=;
sumr=;
sumc=;
for(i=; i<=nr; i++) {
//scanf("%d",&r[i]);
read(r[i]);
sumr+=r[i];
}
for(i=; i<=nc; i++) {
//scanf("%d",&co[i]);
read(co[i]);
sumc+=co[i];
}
ans=;
if(sumr==sumc)farm();
if(ans==) printf("Case #%d: So naive!\n",cas++);
else if(ans!=) {
printf("Case #%d: So young!\n",cas++);
} else {
printf("Case #%d: So simple!\n",cas++);
}
}
//cout<<"end";
return ;
}
2014-10-16 Update
srm菊苣指出这个居然过不了4888,我试了一下,居然真的wa!
然后发现了一个小错误,if(e[i].v==prex)continue,continue之前没有修改biu。加上biu=i就能过了。
之前居然挂了错的代码这么久,见谅!我逗!
hdu4975 A simple Gaussian elimination problem.(正确解法 最大流+删边判环)(Updated 2014-10-16)的更多相关文章
- hdu4975 A simple Gaussian elimination problem.(最大流+判环)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4975 题意:和hdu4888基本一样( http://www.cnblogs.com/a-clown/ ...
- A simple Gaussian elimination problem.(hdu4975)网络流+最大流
A simple Gaussian elimination problem. Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65 ...
- HDOJ 4975 A simple Gaussian elimination problem.
和HDOJ4888是一样的问题,最大流推断多解 1.把ISAP卡的根本出不来结果,仅仅能把全为0或者全为满流的给特判掉...... 2.在残量网络中找大于2的圈要用一种类似tarjian的方法从汇点開 ...
- HDU 4975 A simple Gaussian elimination problem.
A simple Gaussian elimination problem. Time Limit: 1000ms Memory Limit: 65536KB This problem will be ...
- hdu - 4975 - A simple Gaussian elimination problem.(最大流量)
意甲冠军:要在N好M行和列以及列的数字矩阵和,每个元件的尺寸不超过9,询问是否有这样的矩阵,是独一无二的N(1 ≤ N ≤ 500) , M(1 ≤ M ≤ 500). 主题链接:http://acm ...
- A simple Gaussian elimination problem.
hdu4975:http://acm.hdu.edu.cn/showproblem.php?pid=4975 题意:给你一个n*m的矩阵,矩阵中的元素都是0--9,现在给你这个矩阵的每一行和每一列的和 ...
- hdu 4975 A simple Gaussian elimination problem.(网络流,推断矩阵是否存在)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4975 Problem Description Dragon is studying math. One ...
- hdu 4975 A simple Gaussian elimination problem 最大流+找环
原题链接 http://acm.hdu.edu.cn/showproblem.php?pid=4975 这是一道很裸的最大流,将每个点(i,j)看作是从Ri向Cj的一条容量为9的边,从源点除法连接每个 ...
- hdu4975 网络流解方程组(网络流+dfs判环或矩阵DP)
http://acm.hdu.edu.cn/showproblem.php?pid=4975 A simple Gaussian elimination problem. Time Limit: 20 ...
随机推荐
- 【BZOJ-2892&1171】强袭作战&大sz的游戏 权值线段树+单调队列+标记永久化+DP
2892: 强袭作战 Time Limit: 50 Sec Memory Limit: 512 MBSubmit: 45 Solved: 30[Submit][Status][Discuss] D ...
- 【BNUOJ19500】 Matrix Decompressing
https://www.bnuoj.com/v3/problem_show.php?pid=19500 (题目链接) 题意 给出一个R行C列的正整数矩阵,设前${A_i}$项为其前i行所有元素之和,$ ...
- [Xcode 自带svn的使用]
xcode自带svn的使用 1.代码中 某文件后面有 “M” 标记,表示该文件已被修改,需要commit. (右键该文件 -> source control -> c ...
- EF-CodeFirst-3搞事
本文学习旺杰兄的 CodeFirst 系列教程而写.尽量摆脱之前的影子写出自己的理解 表间关系.级联删除 简单玩法已经走通了,但是我就是想搞点事出来.今天来搞搞表间关系和级联删除 表间关系 毫无疑问在 ...
- HDU 1548 A strange lift (最短路/Dijkstra)
题目链接: 传送门 A strange lift Time Limit: 1000MS Memory Limit: 32768 K Description There is a strange ...
- CF 445B DZY Loves Chemistry(并查集)
题目链接: 传送门 DZY Loves Chemistry time limit per test:1 second memory limit per test:256 megabytes D ...
- PTA fzu_oop_east
GitHub链接: 传送门 5-1该日是该年的第几天 定义一个日期类Date,内有数据成员年.月.日,另有成员函数:构造函数用于初始化数据成员,输出,闰年的判断. 编写主函数:创建日期对象,计算并输出 ...
- css007 margin padding border
css007 margin padding border 1.理解盒模型(盒模型:就是把一些东西,包括html各种标签都包含在一个 看不见的盒子里) 1/在web浏览器中任何标签都是一个盒子,内容的周 ...
- 整数分解 && 质因数分解
输入整数(0-30)分解成所有整数之和.每四行换行一次. 一种方法是通过深度优先枚举出解.通过递归的方式来实现. #include <stdio.h> #include <strin ...
- 药企信息sop
中国药品生产企业 http://db.yaozh.com/shengchanqiye 全球药品生产企业 http://db.yaozh.com/quanqiuqiye