以下有两段代码,在第一段中,我将建边设为从 a 数组(离散后的编号)到对应数据的倍数(离散后的编号),这无法通过题目。
但是在第二段代码中,我将建边方向反向,即由对应数据倍数(离散后的编号)到 a 数组(离散后的编号),这样通过了题解。
同机房的大佬说是正着(a 指向其倍数)存在匹配失败的问题,但是显然,既然反着(a 的倍数指向 a )匹配可以成功,那么正着也因该存在成功情况,这不是匹配失败的可接受理由。
仅仅只是建边方向的不同,为什么结果不一样?
第一段代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1010,M = N*N;
int a[N],n;//如题目所述
vector<int> b;//离散化
int h[N],e[M],ne[M],idx;
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}//建边
unordered_map<int,int> id;//离散化
int mat[M],mbt[N];//双向匹配
bool s[M];//与访问vis数组等价
bool dfs(int u){//匈牙利算法
for(int i=h[u],v;~i;i=ne[i]){
v=e[i];
if(s[v]) continue;
s[v]=true;
if(mat[v]==0 || dfs(mat[v])){
mat[v]=u;
mbt[u]=v;//双向匹配
return true;
}
}
return false;
}
ll ans;
int main(){
memset(h,-1,sizeof h);
b.push_back(-1);//init,垫一个,方便后续下标访问
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
for(int k=1;k<=n;k++)
b.push_back(a[i]*k);
}
sort(b.begin(),b.end());
b.erase(unique(b.begin(),b.end()),b.end());
for(int i=1;i<b.size();i++)//从下标1开始,上面垫了一个
id[b[i]]=i;
//离散化
for(int i=1;i<=n;i++)
for(int k=n;k>=1;k--)
add(i,id[a[i]*k]);//正着建边
for(int i=n;i>=1;i--){
memset(s,false,sizeof s);
dfs(i);
}
for(int i=1;i<=n;i++) ans+=b[mbt[i]];//双向匹配的原因
printf("%lld",ans);
return 0;
}
第二段代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1010000,M = N;
int a[N],n;
vector<int> b;
int h[M],e[M],ne[M],idx;
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
unordered_map<int,int> id;
int mat[M],mbt[N];
bool s[M];
bool dfs(int u){
for(int i=h[u],v;~i;i=ne[i]){
v=e[i];
if(s[v]) continue;
s[v]=true;
if(mat[v]==0 || dfs(mat[v])){
mat[v]=u;
mbt[u]=v;
return true;
}
}
return false;
}
ll ans;
int main(){
memset(h,-1,sizeof h);
b.push_back(-1);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
for(int k=1;k<=n;k++)
b.push_back(a[i]*k);
}
sort(b.begin(),b.end());
b.erase(unique(b.begin(),b.end()),b.end());
for(int i=1;i<b.size();i++) id[b[i]]=i;
for(int i=n;i>=1;i--)
for(int k=n;k>=1;k--)
add(id[a[i]*k],i);//反着建边
int cnt=0;
for(int i=0;i<b.size();i++){
if(dfs(i)){
ans+=b[i];
memset(s,false,sizeof s);//一点优化,不足挂齿
cnt++;
if(cnt==n) break;//新的判定方式
}
}
printf("%lld",ans);
return 0;
}