这道题目确实是比较好,当时比赛的时候就一心想着高斯肖元,但是最坏的情况未知数最多为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;
}