【BZOJ 2597】 [Wc2007]剪刀石头布

2597: [Wc2007]剪刀石头布Time Limit:20 SecMemory Limit:128 MBSecSpecial JudgeSubmit:492Solved:227[Submit][Status]Description

(A, B, C)(A, C, B)(C, B, A)视为相同的情况。

有N个人参加一场这样的游戏的比赛,赛程规定任意两个人之间都要进行一场比赛:这样总共有场比赛。比赛已经进行了一部分,我们想知道在极端情况下,比赛结束后最多会发生多少剪刀石头布情况。即给出已经发生的比赛结果,而你可以任意安排剩下的比赛的结果,以得到尽量多的剪刀石头布情况。

Input

N,表示参加比赛的人数。

之后是一个N行N列的数字矩阵:一共N行,每行N列,数字间用空格隔开。

Output

Sample Input

30 1 20 0 22 2 0

Sample Output

10 1 00 0 11 0 0

HINT

100%的数据中,N≤ 100。

费用流。

题目的要求就是对于一个竞赛图,给一些边定向使得长度为3的环最多。

先进行补集转化:使得非三元环的数量最少。

可以发现在三个点中,若一个点入度为2,那么一定无法构成三元环。

ans

=C(n,3)-min(sigma(C(indegree[i],2)))

=n*(n-1)*(n-2)/6+sigma(indegree[i])/2-min(sigma(indegree[i]^2))/2

=n*(n-1)*(n-2)/6+n*(n-1)/4-min(sigma(indegree[i]^2))/2

前两部分是常数,所以最小化sigma(indegree[i]^2)即可。

对于x^2建模方法是连费用为1,3,5,7,9。。。的边,那么连n条边的和就是n^2了(根据n^2-(n-1)^2证明)

对于已知边,,直接从s-t连流量为1,费用为上述建模方法;

每条未知边看作一个点x,从s到x连流量为1,费用为0的边;再从x向对战双方连流量为1费用为0的边。

最后对于每个人向t连若干条流量为1,费用最小为(已知入度k)1+2k,最大为1+2(n-1)的边。

求费用流即可。

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <cstdlib>#define inf 0x3f3f3f3f#define M 100000#include <queue>using namespace std;int cnt=0,tot=1,in[M],n,s,t,h[M],d[M],inq[M],p[M],f[M],a[105][105];struct edge{int from,to,cap,flow,cost,ne;}E[200005];void read(int &tmp){tmp=0;int fu=1;char ch=getchar();for (;ch<'0'||ch>'9';ch=getchar())if (ch=='-') fu=-1;for (;ch>='0'&&ch<='9';ch=getchar())tmp=tmp*10+ch-'0';tmp*=fu;}void Addedge(int from,int to,int cap,int cost){E[++tot]=(edge){from,to,cap,0,cost,h[from]};h[from]=tot;E[++tot]=(edge){to,from,0,0,-cost,h[to]};h[to]=tot;}bool spfa(int &flow,int &cost){for (int i=s;i<=t;i++)d[i]=inf,inq[i]=0;d[s]=0,inq[s]=1,p[s]=0,f[s]=inf;queue<int> q;q.push(s);while (!q.empty()){int x=q.front();q.pop();inq[x]=0;for (int i=h[x];i;i=E[i].ne){edge &e=E[i];if (e.cap>e.flow&&d[e.to]>d[x]+e.cost){d[e.to]=d[x]+e.cost;p[e.to]=i;f[e.to]=min(f[x],e.cap-e.flow);if (!inq[e.to]) q.push(e.to),inq[e.to]=1;}}}if (d[t]==inf) return false;flow+=f[t];cost+=d[t]*f[t];int u=t;while (u!=s){E[p[u]].flow+=f[t];E[p[u]^1].flow-=f[t];u=E[p[u]].from;}return true;}int mincost(){int flow=0,cost=0;while (spfa(flow,cost));return cost;}void Print(){int now=0;for (int i=1;i<=n;i++)for (int j=i+1;j<=n;j++)if (a[i][j]==2){now++;for (int k=h[now];k;k=E[k].ne)if (E[k].flow==1&&(E[k].to==cnt+i||E[k].to==cnt+j)){if (E[k].to==cnt+i) a[i][j]=0,a[j][i]=1;else a[i][j]=1,a[j][i]=0;}}for (int i=1;i<=n;i++){for (int j=1;j<n;j++)printf("%d ",a[i][j]);printf("%d\n",a[i][n]);}}int main(){read(n);cnt=0;s=0;for (int i=1;i<=n;i++)for (int j=1;j<=n;j++){read(a[i][j]);if (i<j&&a[i][j]==2)cnt++;if (a[i][j]==1)in[j]++;}t=cnt+n+1;int now=0;for (int i=1;i<=n;i++)for (int j=i+1;j<=n;j++)if (a[i][j]==2)Addedge(s,++now,1,0),Addedge(now,cnt+i,1,0),Addedge(now,cnt+j,1,0);for (int i=1;i<=n;i++){int k=1;now=0;while (now<in[i]*in[i]){Addedge(s,t,1,k);now+=k,k+=2;}for (;k<=2*n;k+=2)Addedge(cnt+i,t,1,k);}int k=(n*(n-1))/4-mincost()/2;printf("%d\n",n*(n-1)*(n-2)/6+k);Print();return 0;}

感悟:

1.tle是在连边的地方<in[i]*in[i]写成<in[i]了

2.对于x^2的建模是练1,3,5,7,9。。。的边

人生至少要有两次冲动,一为奋不顾身的爱情,一为说走就走的旅行。

【BZOJ 2597】 [Wc2007]剪刀石头布

相关文章:

你感兴趣的文章:

标签云: