https://cn.vjudge.net/problem/UVA-1627
题目
有n(n≤100)个人,把他们分成非空的两组,使得每个人都被分到一组,且同组中的人相互认识。要求两组的成员人数尽量接近。多解时输出任意方案,无解时输出No Solution。
例如,1认识2, 3, 5;2认识1, 3, 4, 5;3认识1, 2, 5,4认识1, 2, 3,5认识1, 2, 3, 4(注意4认识1但1不认识4),则可以分两组:{1,3,5}和{2,4}。
题解
不是互相认识的连边,然后保证一条边的两个人分到两个不同的组
一个连通分量的人可以存起来……那么类似于poj1417最后的dp部分
设$dp[i][j]$为前i个连通块,两组人数差为j时是否存在
因为j可能为负数,不方便迭代,于是改为设$dp[i][100+j]$
易证若差为j时存在,那么差为-j时也存在,于是最后只用枚举$dp[conn][100..100+n]$是否存在,然后按照dp相反的方向输出解(因为要看每一次的决策是什么)
AC代码
#include<bits/stdc++.h> using namespace std; #define REP(r,x,y) for(register int r=(x); r<(y); r++) #define PER(r,x,y) for(register int r=(x); r>(y); r--) #define REPE(r,x,y) for(register int r=(x); r<=(y); r++) #define PERE(r,x,y) for(register int r=(x); r>=(y); r--) #ifdef sahdsg #define DBG(...) printf(__VA_ARGS__) #else #define DBG(...) (void)0 #endif int n; int G[107][107]; bool tmp[107]; int v[107],conn=0,r[107]; int vis[107]; bool failed; int dp[107][207]; int cho[107]; inline int signx(int x, int k) { int ans=1; int t=x+1; if(t>n) t=1; vis[x]=k; do { if(G[x][t]) { if(!vis[t]) ans-=signx(t,-k); else if(vis[t]!=-k) { failed = true; return 0; } } if(failed) return 0; t++; if(t>n) t=1; } while(t!=x); return ans; } int ansarr[107][2], ansn[2]={0,0}; inline void anx(int x, int i) { ansarr[ansn[i]++][i]=x; int t=x+1; if(t>n) t=1; vis[x]=-2; do { if(G[x][t] && vis[t]!=-2) { anx(t, !i); } t++; if(t>n) t=1; } while(t!=x); } int main() { #ifdef sahdsg freopen("in.txt","r",stdin); #endif int t; scanf("%d", &t); while(0<t--) { scanf("%d", &n); memset(G,0,sizeof G); memset(vis,0,sizeof vis); conn=0; REPE(i,1,n) { int v; memset(tmp,0,sizeof tmp); tmp[i]=1; while(1) { scanf("%d", &v); if(v==0) break; tmp[v]=1; } REPE(j,1,n) if(tmp[j]==0) G[i][j]=G[j][i]=1; } failed = false; int a=0; REPE(i,1,n) { if(!vis[i]) {r[++conn]=i; v[conn]=signx(i,1);} if(failed) { goto _wa; } } #define in(x) ((x)<=200 && (x)>=0) memset(dp,0,sizeof dp); dp[0][100]=1; REPE(i,1,conn) { REPE(j,100-n,100+n) { if(in(j-v[i]) && dp[i-1][j-v[i]]) dp[i][j]=1; else if(in(j+v[i]) && dp[i-1][j+v[i]]) dp[i][j]=1; } } memset(ansn,0,sizeof ansn); for(; a<=n; a++) { if(dp[conn][100+a]) { break; } } PERE(i,conn,1) { if(in(100+a-v[i]) && dp[i-1][100+a-v[i]]) a-=v[i],anx(r[i],0); else if(in(100+a+v[i]) && dp[i-1][100+a+v[i]]) a+=v[i],anx(r[i],1); } #undef in REPE(j,0,1) { printf("%d", ansn[j]); REP(i,0,ansn[j]) { printf(" %d", ansarr[i][j]); } putchar(' '); } if(t) putchar(' '); continue; _wa: puts("No solution"); if(t) putchar(' '); continue; } return 0; }