题解 P2175 【小Z的游戏分队】
以不信任关系建图
二分图判定染色
cnt[i][0/1]表示第i个连通块染1/0颜色的个数
f[i][j]表示前i个连通块第一个集合点的个数减第二个集合点的个数等于j是否可以达到(注意j可能为负 #define f(i,j) dp[i][j+2000])
f(i,j)|=f(i-1,j-cnt[i][0]+cnt[i][1])| f(i-1,j+cnt[i][0]-cnt[i][1]) 对于第i个连通块两种情况:
(1) 颜色为1 的点放入第1个集合,颜色为2 的点放入第2个集合
(2) 颜色为1 的点放入第2个集合,颜色为2 的点放入第1个集合
PS:这里解释一下为什么这道题不能用Tarjan强连通分量,因为信任关系不具有传递性。
#include <cstdio>
#include <iostream>
using namespace std;
const int N=2005;
int n,x,m,color[N],cnt[N][3],f[N][N+N];
bool flag[N][N];
int tot,head[N],next[N*N],adj[N*N];
inline int read(int &x){
char c=getchar();int num=0;
for(;c<'0'||c>'9';c=getchar());
for(;c>='0'&&c<='9';c=getchar()) num=(num<<1)+(num<<3)+c-'0';
x=num;
}
void addedge(int u,int v){
++tot;
next[tot]=head[u];
head[u]=tot;
adj[tot]=v;
}
bool dfs(int u){//二分图染色
if(color[u]==1) cnt[m][1]++;
else cnt[m][2]++;
int e,v;
for(e=head[u];e;e=next[e]){
v=adj[e];
if(!color[v]){
color[v]=3-color[u];
dfs(v);
}
else if(color[v]!=3-color[u]) return false;
}
return true;
}
int main(){
read(n);
for(int i=1;i<=n;i++)
while(1){
read(x);
if(x==0) break;
flag[i][x]=true;
}
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(!flag[i][j]||!flag[j][i]){
addedge(i,j);
addedge(j,i);
}
for(int i=1;i<=n;i++)
if(!color[i]){
++m;
color[i]=1;
if(!dfs(i)){
printf("No solution");
return 0;
}
}
f[0][0]=1;
for(int i=1;i<=m;i++)
for(int j=-2000;j<=2000;j++)//枚举第一个集合点的个数与第二个集合点的差值
f[i][j+2000]|=f[i-1][j-cnt[i][1]+cnt[i][2]+2000]|f[i-1][j+cnt[i][1]-cnt[i][2]+2000];
int x,y;
for(int j=0;j<=2000;j++)
if(f[m][j+2000]||f[m][j]){ //枚举第一个集合点的个数与第二个集合点的差的绝对值
x=(n+j)/2;
y=n-x;
if(x>y) swap(x,y);
printf("%d %d\n",x,y);
return 0;
}
printf("No solution");
return 0;
}