spoj 1693 Coconuts( 违背自己意愿的票数与持不同意见的朋友对数的总和最小)

本文介绍了一道关于N个城堡守卫投票的题目,通过建立图模型,利用最小割算法来解决该问题。具体地,将每个守卫的初始观点转化为图中的边,并考虑守卫之间的朋友关系,最终求出违背意愿投票数和朋友间不同意见对数之和的最小值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考:http://wenku.baidu.com/link?url=E5J_eujJ5h0P08UFEWi6PCp9ZX97MM-1NMxUfbdAEx48yLQWqZk4LlMwrG8Wt6Ks1V5hzoWDurZcvRSjCZ1OQ9ZmMmvEkbqP8Fuz489h3PS

传送门:spoj 1693 Coconuts


题意:
N 个城堡守卫正在就非洲的燕子能否搬运椰子而进行投票。每个人都有自己的看法,但是为了避免跟自己的朋友持相反意见,他们
时常会投相反的票。现在给出每个人的初始看法以及朋友关系,求在某种投票方案下, 违背自己意愿的票数与持不同意见的朋友对数的总和最小。 (2 <= N <= 300,1 <= M <= N(N-1)/2)


思路:
和spoj 839比较类似,S和原来为0的点连边,t和原来为1的点,原来的边(u,v)->大的多个addedge(u,v,1),addedge(v,u,1),求最小割就可以了

我们再来考虑一下最小割的意义,割边如果是与s或者t相连,那么表示这个点变色,如果不是,表示两边的颜色都不变


:E(x1,x2,,xn)=(1xi)|piv0|+xi|piv1|+|xixj||pipj|,pi,v0,v1xi0,1

,xi=0|piv0|
xi=1|piv1|xi,xj|pipj|

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define inf -0x3f3f3f3f
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mem0(a) memset(a,0,sizeof(a))
#define mem1(a) memset(a,-1,sizeof(a))
#define mem(a, b) memset(a, b, sizeof(a))
#define MP(x,y) make_pair(x,y)
typedef long long ll;
void fre() { freopen("input.in", "r", stdin); freopen("output.out", "w", stdout); }
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }

const int maxn=310;
const int MAXM=1001000;
int head[maxn],tot,cur[maxn];
struct Edge{
    int to,next,cap,flow;
}e[MAXM];
int d[maxn],gap[maxn],N,S[MAXM];

void init(int n){
    memset(head,-1,sizeof(head));
    tot=0;
    N=n;
}

void addedge(int from,int to,int w){
    e[tot].to=to,e[tot].next=head[from];
    e[tot].cap=w,e[tot].flow=0;
    head[from]=tot++;
    e[tot].to=from,e[tot].next=head[to];
    e[tot].cap=0,e[tot].flow=0;
    head[to]=tot++;
}

void bfs(int st){
    memset(d,-1,sizeof(d));
    memset(gap,0,sizeof(gap));
    d[st]=0,gap[0]=1;
    queue<int>Q;
    Q.push(st);
    while(!Q.empty()){
        int u=Q.front();
        Q.pop();
        for(int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].to;
            if(d[v]!=-1)
                continue;
            d[v]=d[u]+1;
            gap[d[v]]++;
            Q.push(v);
        }
    }
}

int sap(int st,int ed){
    memcpy(cur,head,sizeof(head));
    bfs(ed);
    int u=st,top=0,ans=0;
    while(d[st]<N){
        if(u==ed){
            int Min=INF,ind;
            for(int i=0;i<top;i++){
                if(Min>e[S[i]].cap-e[S[i]].flow){
                    Min=e[S[i]].cap-e[S[i]].flow;
                    ind=i;
                }
            }
            for(int i=0;i<top;i++){
                e[S[i]].flow+=Min;
                e[S[i]^1].flow-=Min;
            }
            top=ind;
            u=e[S[top]^1].to;
            ans+=Min;
        }
        bool flag=false;
        int v;
        for(int i=cur[u];i!=-1;i=e[i].next){
            v=e[i].to;
            if(e[i].cap>e[i].flow&&d[v]+1==d[u]){
                flag=true;
                cur[u]=i;
                break;
            }
        }
        if(flag){
            S[top++]=cur[u];
            u=v;
            continue;
        }
        int Min=N;
        for(int i=head[u];i!=-1;i=e[i].next){
            v=e[i].to;
            if(e[i].cap>e[i].flow&&d[v]<Min){
                Min=d[v];
                cur[u]=i;
            }
        }
        gap[d[u]]--;
        if(gap[d[u]]==0)
            return ans;
        d[u]=Min+1;
        gap[d[u]]++;
        if(u!=st)
            u=e[S[--top]^1].to;
    }
    return ans;
}

int main(){
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF){
        if(n==0&&m==0)
            break;
        int s=0,t=n+1,x,u,v;
        init(t+1);
        for(int i=1;i<=n;i++){
            scanf("%d",&x);
            if(x==0)
                addedge(s,i,1);
            else
                addedge(i,t,1);
        }
        for(int i=1;i<=m;i++)
            scanf("%d%d",&u,&v),addedge(u,v,1),addedge(v,u,1);
        printf("%d\n",sap(s,t));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值