• bzoj 2597 [Wc2007]剪刀石头布——费用流


    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2597

    三个人之间的关系,除了“剪刀石头布”,就是有一个人赢了2局;所以考虑算补集,则每个人对答案的贡献是 ( -C_{f[ i ]}^{2} = frac{f[ i ]*(f[ i ]-1)}{2}) ,其中 f[ i ] 表示这个人赢的局数。

    所以一个人多赢了一局,对答案的贡献是 -f[ i ] ;再多赢一局,就是 -( f[ i ] + 1 ) ……只要每个人向汇点连足够的边,其中每条边容量是1、费用依次为 f[ i ] , f[ i ]+1 , …… 就行了,因为会先走费用小的,符合意义。

    对于每场未确定比赛,新建一个点,从源点向它连容量为1、费用为0的边;然后从它分别向两个人连容量为1、费用为0的边,表示这场比赛会令其中一个人增加费用。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    const int N=105,M=4955,INF=M;
    int n,cnt,f[N],c[N],ans,hd[N+M],xnt=1,b[N][N],dy[M];
    int dis[N+M],pre[N+M],incf[N+M];bool vis[N+M];
    struct Ed{
      int fr,to,nxt,cap,w;
      Ed(int f=0,int a=0,int b=0,int c=0,int d=0):fr(f),to(a),nxt(b),cap(c),w(d) {}
    }ed[(N*N+M*3)<<1];
    queue<int> q;
    int Mn(int a,int b){return a<b?a:b;}
    void add(int x,int y,int z,int w)
    {
      ed[++xnt]=Ed(x,y,hd[x],z,w);hd[x]=xnt;
      ed[++xnt]=Ed(y,x,hd[y],0,-w);hd[y]=xnt;
    }
    bool spfa()
    {
      memset(dis,0x3f,sizeof dis);
      dis[0]=0;vis[0]=1;q.push(0);
      pre[cnt]=0;incf[0]=INF;
      while(q.size())
        {
          int k=q.front();q.pop();vis[k]=0;
          for(int i=hd[k],v;i;i=ed[i].nxt)
        if(ed[i].cap&&dis[v=ed[i].to]>dis[k]+ed[i].w)
          {
            dis[v]=dis[k]+ed[i].w;pre[v]=i;
            incf[v]=Mn(incf[k],ed[i].cap);
            if(!vis[v])q.push(v),vis[v]=1;
          }
        }
      return pre[cnt];
    }
    void ek()
    {
      int ret=incf[cnt];
      for(int k=pre[cnt];k;k=pre[ed[k].fr])
        {
          ed[k].cap-=ret;ed[k^1].cap+=ret;
          ans-=ed[k].w*ret;
        }
    }
    int main()
    {
      scanf("%d",&n);cnt=n;int val=0;
      for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
          {
        scanf("%d",&b[i][j]);if(i>=j)continue;
        if(b[i][j]==1)f[i]++; else if(!b[i][j])f[j]++;
        else
          {
            c[i]++;c[j]++;cnt++;val++;
            add(0,cnt,1,0);add(cnt,i,1,0);add(cnt,j,1,0);
            dy[cnt-n]=xnt-1;
          }
          }
      cnt++; ans=n*(n-1)*(n-2)/6;
      for(int i=1;i<=n;i++)
        {
          ans-=f[i]*(f[i]-1)/2;
          for(int j=0;j<c[i];j++)add(i,cnt,1,f[i]+j);
        }
      while(spfa())ek();
      for(int i=1,p=0;i<=n;i++)
        for(int j=1;j<=n;j++)
          {
        if(i>=j||b[i][j]<2)continue;
        p++;
        if(ed[dy[p]].cap)b[i][j]=1,b[j][i]=0;
        else b[i][j]=0,b[j][i]=1;
          }
      printf("%d
    ",ans);
      for(int i=1;i<=n;i++,puts(""))
        for(int j=1;j<=n;j++)printf("%d ",b[i][j]);
      return 0;
    }
  • 相关阅读:
    swift 图像的压缩上传
    swift UILabel加载html源码
    UITableViewCell上面添加UIWebView
    iOS 富文本点击事件
    iOS 导航栏 不透明
    【异步编程】理解异步
    使用 Git Bash
    【选择符 API】querySelector() 方法
    Vue 模板语法-指令
    Vue 模板语法-插值
  • 原文地址:https://www.cnblogs.com/Narh/p/10118256.html
Copyright © 2020-2023  润新知