https://www.luogu.org/problemnew/show/P2057
幼儿园里有n个小朋友打算通过投票来决定睡不睡午觉。对他们来说,这个问题并不是很重要,于是他们决定发扬谦让精神。虽然每个人都有自己的主见,但是为了照顾一下自己朋友的想法,他们也可以投和自己本来意愿相反的票。我们定义一次投票的冲突数为好朋友之间发生冲突的总数加上和所有和自己本来意愿发生冲突的人数。
我们的问题就是,每位小朋友应该怎样投票,才能使冲突数最小?
输入输出格式
输入格式:文件的第一行只有两个整数n,m,保证有2≤n≤300,1≤m≤n(n-1)/2。其中n代表总人数,m代表好朋友的对数。文件第二行有n个整数,第i个整数代表第i个小朋友的意愿,当它为1时表示同意睡觉,当它为0时表示反对睡觉。接下来文件还有m行,每行有两个整数i,j。表示i,j是一对好朋友,我们保证任何两对i,j不会重复。
输出格式:只需要输出一个整数,即可能的最小冲突数。
输入输出样例
说明
2≤n≤300,1≤m≤n(n-1)/2。
题解:要么投睡觉要么不睡,所以应分成两个集合,因为可以跟着好朋友改意见,所以可以连着边的人都可以投同一个票。每个人只有两种选择,所以肯定和s点或t点连着,所以如果割变是与源汇点连着,则算是与自己的意愿冲突,如果割在朋友间则是与朋友的冲突。每割断一对好友或自身意愿。冲突数加一,所以就是求最小割。最小割等于最大流。
注意的是一对好友之间可以互相改意见,所以直接建双向边(注意是双向边,和原来算法的反向边区分好,这是本题需要,反向边是为了保证算法正确,都要建,刚开始我就被坑了)。自己再建一个s点表示一种最初观点相同的人,建一个t点表示另一种。我是投1的连s,投0的人连t。
注意的是一对好友之间可以互相改意见,所以直接建双向边(注意是双向边,和原来算法的反向边区分好,这是本题需要,反向边是为了保证算法正确,都要建,刚开始我就被坑了)。自己再建一个s点表示一种最初观点相同的人,建一个t点表示另一种。我是投1的连s,投0的人连t。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<set> #include<algorithm> #include<map> #define maxn 200005 typedef long long ll; #define inf 1000000009 int x,y,num; using namespace std; struct edge{ int to,cap,rev; //边的终点,容量,反向边 }; vector<edge>mp[maxn]; // 邻接图 bool vis[maxn]; void add_edge(int from,int to,int cap) //建图 { mp[from].push_back((edge){to,cap,mp[to].size()}); mp[to].push_back((edge){from,0,mp[from].size()-1}); //反向弧 } int dfs(int v,int t,int f) //找增广路 v,t是最终点 用了f的流量 { if(v==t)return f; vis[v]=true; for(int i=0;i<mp[v].size();i++) { edge &e=mp[v][i]; if(!vis[e.to]&&e.cap>0) { int d=dfs(e.to,t,min(f,e.cap)); //遍历所有的路径 if(d>0) { e.cap-=d; //求增加的流量 mp[e.to][e.rev].cap+=d; return d; } } } return 0; } int max_flow(int s,int t) { int flow=0; for(;;) { memset(vis,0,sizeof(vis)); //初始化 int f=dfs(s,t,inf); if(f==0)return flow; flow+=f; } } int main() { int n,m,s; cin>>n>>m; for(int i=1;i<=n;i++){ cin>>s; if(s==1)add_edge(i,306,1); else add_edge(305,i,1); } for(int i=0;i<m;i++) { cin>>x>>y; add_edge(x,y,1); add_edge(y,x,1); } int ans=max_flow(305,306); cout<<ans<<endl; return 0; }
dina代码:
<span style="font-size:12px;"><strong>#include <cstdio> #include <string.h> #include <queue> using namespace std; int const inf = 0x3f3f3f3f; int const MAX = 205; int n, m; int c[MAX][MAX], dep[MAX];//dep[MAX]代表当前层数 int bfs(int s, int t)//重新建图,按层次建图 { queue<int> q; while(!q.empty()) q.pop(); memset(dep, -1, sizeof(dep)); dep[s] = 0; q.push(s); while(!q.empty()){ int u = q.front(); q.pop(); for(int v = 1; v <= m; v++){ if(c[u][v] > 0 && dep[v] == -1){//如果可以到达且还没有访问,可以到达的条件是剩余容量大于0,没有访问的条件是当前层数还未知 dep[v] = dep[u] + 1; q.push(v); } } } return dep[t] != -1; } int dfs(int u, int mi, int t)//查找路径上的最小流量 { if(u == t) return mi; int tmp; for(int v = 1; v <= m; v++){ if(c[u][v] > 0 && dep[v] == dep[u] + 1 && (tmp = dfs(v, min(mi, c[u][v]), t))){ c[u][v] -= tmp; c[v][u] += tmp; return tmp; } } return 0; } int dinic() { int ans = 0, tmp; while(bfs(1, m)){ while(1){ tmp = dfs(1, inf, m); if(tmp == 0) break; ans += tmp; } } return ans; } int main() { while(~scanf("%d %d", &n, &m)){ memset(c, 0, sizeof(c)); int u, v, w; while(n--){ scanf("%d %d %d", &u, &v, &w); c[u][v] += w; } printf("%d ", dinic()); } return 0; }</strong></span>