P2057 [SHOI2007]善意的投票 / [JLOI2010]冠军调查 [最小割] [二分图]
题目描述
幼儿园里有 nn 个小朋友打算通过投票来决定睡不睡午觉。
对他们来说,这个问题并不是很重要,于是他们决定发扬谦让精神。
虽然每个人都有自己的主见,但是为了照顾一下自己朋友的想法,他们也可以投和自己本来意愿相反的票。
我们定义一次投票的冲突数为好朋友之间发生冲突的总数加上和所有和自己本来意愿发生冲突的人数。
我们的问题就是,每位小朋友应该怎样投票,才能使冲突数最小?
输入格式
第一行两个整数 n,mn,m。其中 nn 代表总人数,mm 代表好朋友的对数。
第二行 nn 个整数,第 ii 个整数代表第 ii 个小朋友的意愿:当它为 11 时表示同意睡觉,当它为 00 时表示反对睡觉。
接下来 mm 行,每行有两个整数 i,ji,j,表示 i,ji,j 是一对好朋友,我们保证任何两对 i,ji,j 不会重复。
输出格式
一行一个整数,即可能的最小冲突数。
输入输出样例
输入 #1
3 3 1 0 0 1 2 1 3 3 2
输出 #1
1
思路
题目要求最小的冲突数,实际上就是问把所有人分成两个互不相干的阵营的最小花费。
注意到最后形成的其实是一个支持睡觉和不支持睡觉的二分图。
所以设置一个源点表示支持睡觉的阵营,一个汇点表示不支持的阵营。
对于每个人,可以加入朋友的阵营,也可以选择坚持不变。
所以在朋友之间双向连边。
之后只要算出把阵营分成一个二分图的最小割就可以了。
CODE
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<queue> 5 #include<vector> 6 #include<algorithm> 7 8 using namespace std; 9 10 const int maxn = 1e6 + 7; 11 const int inf = 0x3f3f3f3f; 12 13 template<class T>inline void read(T &res) 14 { 15 char c;T flag=1; 16 while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0'; 17 while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag; 18 } 19 20 struct edge{int from,to,cap,flow;}; 21 22 struct isap 23 { 24 int n,s,t,p[maxn],d[maxn],cur[maxn],num[maxn]; 25 bool vis[maxn]; 26 vector<int>g[maxn]; 27 vector<edge>edges; 28 void init(int n,int s,int t) { 29 this->n = n; 30 this->s = s; 31 this->t = t; 32 for(int i = 1;i <= n;i++) g[i].clear(); 33 edges.clear(); 34 } 35 void addegde(int from,int to,int cap) { 36 edges.push_back((edge){from, to, cap, 0}); 37 edges.push_back((edge){to, from, 0, 0}); 38 int m = edges.size(); 39 g[from].push_back(m-2); 40 g[to].push_back(m-1); 41 } 42 43 int augment() {///找增广路 44 int x = t,a = inf; 45 while(x!=s) { 46 a = min(a, edges[p[x]].cap - edges[p[x]].flow); 47 x = edges[p[x]].from; 48 } 49 x=t; 50 while(x != s) { 51 edges[p[x]].flow += a; 52 edges[p[x]^1].flow = -a; 53 x = edges[p[x]].from; 54 } 55 return a; 56 } 57 int maxflow() {///更新最大流 58 int flow = 0; 59 memset(num, 0, sizeof(num)); 60 memset(cur, 0, sizeof(cur)); 61 for(int i = 1; i <= n; i++) num[d[i]]++; 62 int x = s; 63 while(d[s] < n) {///最长的一条链上,最大的下标是nv-1,如果大于等于nv说明已断层 64 if(x == t) { 65 flow += augment(); 66 x = s;//回退 67 } 68 bool ok = 0; 69 for(int i = cur[x]; i < g[x].size(); i++) { 70 edge &e = edges[g[x][i]]; 71 if(d[x] == d[e.to] + 1 && e.cap > e.flow) { 72 p[e.to] = g[x][i]; 73 cur[x] = i;x = e.to; 74 ok = 1; 75 break; 76 } 77 } 78 if(!ok) { 79 int m = n-1; 80 for(int i = 0; i < g[x].size();i++) { 81 edge &e=edges[g[x][i]]; 82 if(e.cap>e.flow) m=min(m,d[e.to]); 83 } 84 num[d[x]]--; 85 if(!num[d[x]]) break; 86 d[x] = m+1; 87 num[d[x]]++; 88 cur[x] = 0; 89 if(x != s) x = edges[p[x]].from; 90 } 91 } 92 return flow; 93 } 94 }ISAP; 95 96 int n, p, q; 97 int s, t; 98 int tot = 0; 99 int m; 100 101 int main() 102 { 103 read(n); read(m); 104 s = n * (n - 1); 105 t = s + 1; 106 ISAP.init(n * n, s, t); 107 for ( int i = 1; i <= n; ++i ) { 108 int x; 109 read(x); 110 if(!x) { 111 ISAP.addegde(i, t, 1); 112 } 113 else { 114 ISAP.addegde(s, i, 1); 115 } 116 } 117 for ( int i = 1; i <= m; ++i ) { 118 int u, v; 119 read(u); read(v); 120 ISAP.addegde(u, v, 1); 121 ISAP.addegde(v, u, 1); 122 } 123 cout << ISAP.maxflow() << endl; 124 return 0; 125 }
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1e6 + 7;
const int inf = 0x3f3f3f3f;
template<class T>inline void read(T &res)
{
char c;T flag=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}
struct edge{int from,to,cap,flow;};
struct isap
{
int n,s,t,p[maxn],d[maxn],cur[maxn],num[maxn];
bool vis[maxn];
vector<int>g[maxn];
vector<edge>edges;
void init(int n,int s,int t) {
this->n = n;
this->s = s;
this->t = t;
for(int i = 1;i <= n;i++) g[i].clear();
edges.clear();
}
void addegde(int from,int to,int cap) {
edges.push_back((edge){from, to, cap, 0});
edges.push_back((edge){to, from, 0, 0});
int m = edges.size();
g[from].push_back(m-2);
g[to].push_back(m-1);
}
int augment() {///找增广路
int x = t,a = inf;
while(x!=s) {
a = min(a, edges[p[x]].cap - edges[p[x]].flow);
x = edges[p[x]].from;
}
x=t;
while(x != s) {
edges[p[x]].flow += a;
edges[p[x]^1].flow = -a;
x = edges[p[x]].from;
}
return a;
}
int maxflow() {///更新最大流
int flow = 0;
memset(num, 0, sizeof(num));
memset(cur, 0, sizeof(cur));
for(int i = 1; i <= n; i++) num[d[i]]++;
int x = s;
while(d[s] < n) {///最长的一条链上,最大的下标是nv-1,如果大于等于nv说明已断层
if(x == t) {
flow += augment();
x = s;//回退
}
bool ok = 0;
for(int i = cur[x]; i < g[x].size(); i++) {
edge &e = edges[g[x][i]];
if(d[x] == d[e.to] + 1 && e.cap > e.flow) {
p[e.to] = g[x][i];
cur[x] = i;x = e.to;
ok = 1;
break;
}
}
if(!ok) {
int m = n-1;
for(int i = 0; i < g[x].size();i++) {
edge &e=edges[g[x][i]];
if(e.cap>e.flow) m=min(m,d[e.to]);
}
num[d[x]]--;
if(!num[d[x]]) break;
d[x] = m+1;
num[d[x]]++;
cur[x] = 0;
if(x != s) x = edges[p[x]].from;
}
}
return flow;
}
}ISAP;
int n, p, q;
int s, t;
int tot = 0;
int m;
int main()
{
read(n); read(m);
s = n * (n - 1);
t = s + 1;
ISAP.init(n * n, s, t);
for ( int i = 1; i <= n; ++i ) {
int x;
read(x);
if(!x) {
ISAP.addegde(i, t, 1);
}
else {
ISAP.addegde(s, i, 1);
}
}
for ( int i = 1; i <= m; ++i ) {
int u, v;
read(u); read(v);
ISAP.addegde(u, v, 1);
ISAP.addegde(v, u, 1);
}
cout << ISAP.maxflow() << endl;
return 0;
}