题目链接:uestc 1651 Fill Numbers
这道题目确实是比较好,当时比赛的时候就一心想着高斯肖元,但是最坏的情况未知数最多为1000,而我的高斯肖元模版的时间复查度是O(n^3)几乎不可搞。。。因为如果高斯肖元的话,每个方程最多才2个未知数,一定可以得到更优的算法
把空格看作点,如果一行有 2 个空格,就在这两个空格之间连一条边,如果
列有2 个空格同理。这样最后形成的图可以分为以下几种连通块
1、一个孤立的点,说明这个点的值直接受所在行和所在列的约束,因此这
点可能有唯一解,也可能无解。
2、一条链,两个端点均为度为 1 的点,因此两个端点的值都可以通过所在
行或列唯一确定,我们从一端开始 dfs,依次求出链中每个点的值,最后判断一
下另一个端点的值是否合法即可。在这种情况下也是可能有唯一解,或者无解。
3、一个环,仔细思考后可以发现,对一个环,只要存在一组特解,就一定
可以构造出无穷多组可行解,因此我们任取一点,任取一个初值开始 dfs,把环
拆成链,求出环中的一组特解,如果特解存在,则有无穷解,否则无解。
具体操作可以先从度为 1 的点开始 dfs,向空格填入对应的解,然后处理所
有度为0 的孤立点,最后判断有没有环,如果有环的环填入一组特解。最后统一
判断解是否合法
1、解合法&&无环 Unique
2、解合法&&有环 More than one
3、解不合法 No solution
证明第3点其实很好证明:
如果存在环的话,那么这个环内的定点个数就是偶数个,假如设 1 -> 2 -> 3 ->4 -> 1 是个环,而(1,2)的和值a,(2,3)和值为b,(3,4)和值为c,(4,1)和值为d, 那么只要满足
a+ c = b + d 就一定存在无穷多节,否者就无解
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <vector> using namespace std; const int maxn=1010; int n,m,a[maxn][maxn],row[maxn],col[maxn]; int x[maxn*2],y[maxn*2],degree[maxn*2]; vector<int> g[maxn*2]; bool vis[maxn*2]; int cal(int u) { int i; for(i=0;i<m;i++) if(i!=y[u]&&a[x[u]][i]==-1) break; if(i>=m){ int tmp=0; for(int j=0;j<m;j++) if(j!=y[u]) tmp+=a[x[u]][j]; return row[x[u]]-tmp; } int tmp=0; for(i=0;i<n;i++) if(i!=x[u]) tmp+=a[i][y[u]]; return col[y[u]]-tmp; } int Cal(int u,int v) { if(x[u]==x[v]){ int tmp=0; for(int i=0;i<m;i++) if(i!=y[u]&&i!=y[v]) tmp+=a[x[u]][i]; return row[x[u]]-tmp; } int tmp=0; for(int i=0;i<n;i++) if(i!=x[u]&&i!=x[v]) tmp+=a[i][y[u]]; return col[y[u]]-tmp; } int last,last_v; void dfs(int u,int va) { vis[u]=1; last=u,last_v=va; int sz=g[u].size(); for(int i=0;i<sz;i++) { int v=g[u][i]; if(!vis[v]){ int val=Cal(u,v)-va; dfs(v,val); } } } int main() { int T,ca=1; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=0;i<n*m;i++) g[i].clear(); for(int i=0;i<n;i++) for(int j=0;j<m;j++) scanf("%d",&a[i][j]); for(int i=0;i<n;i++) scanf("%d",&row[i]); for(int i=0;i<m;i++) scanf("%d",&col[i]); bool ok=1; for(int i=0;i<n;i++) { bool have=0; int tmp=0; for(int j=0;j<m;j++) if(a[i][j]==-1) { have=1;break; } else tmp+=a[i][j]; if(!have&&tmp!=row[i]) { ok=0;break; } } printf("Case #%d: ",ca++); if(!ok) { puts("No solution");continue; } for(int j=0;j<m;j++) { bool have=0; int tmp=0; for(int i=0;i<n;i++) if(a[i][j]==-1) {have=1;break;} else tmp+=a[i][j]; if(!have&&tmp!=col[j]) { ok=0;break;} } if(!ok) { puts("No solution");continue; } int id=0; for(int i=0;i<n;i++) for(int j=0;j<m;j++) if(a[i][j]==-1) x[id]=i,y[id++]=j; memset(degree,0,sizeof(degree)); memset(vis,0,sizeof(vis)); for(int i=0;i<id;i++) for(int j=i+1;j<id;j++) if(x[i]==x[j]||y[i]==y[j]) { g[i].push_back(j); g[j].push_back(i); degree[i]++,degree[j]++; } for(int i=0;i<id;i++) if(!vis[i]&°ree[i]==1) { int val=cal(i); dfs(i,val); if(cal(last)!=last_v) { ok=0;break;} } if(!ok) { puts("No solution"); continue; } bool multi=0; for(int i=0;i<id;i++) if(!vis[i]&°ree[i]==2) { int sum=Cal(i,g[i][0]); vis[i]=1; dfs(g[i][0],sum); if(Cal(last,i)==last_v) multi=1; else ok=0; } if(!ok) puts("No solution"); else if(ok&&multi) puts("More than one"); else puts("Unique"); } return 0; }