传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1449
http://www.lydsy.com/JudgeOnline/problem.php?id=2597
思路:首先是球队收益,有输有赢不好处理
先假设后面所有比赛都是输,算出收益
然后计算未进行的比赛会带来多少收益
首先从S向每场比赛连边,容量为1,表示比赛只能有一个队赢,费用为0
每场比赛向比赛的两个队连边,容量为1,费用为0
关键是怎么计算每个队增加的收益
拆边,把每多赢一场增加的收益作为费用,容量为1
现在一个队已经赢了win[i]场,输了lose[i]+rem[i](剩下未比赛的场数)场
现在它的收益为C[i]*win[i]^2+D[i]*(lose[i]+rem[i])^2
那么它如果多赢了一场
收益为C[i]*(win[i]+1)^2+D[i]*(lose[i]+rem[i]-1)^2
做差可得2*C[i]*win[i]-2*D[i]*(lose[i]+rem[i])+C[i]+D[i]
这就是它在剩余的比赛中赢第一场的收益
同样,它在剩余比赛中赢的第j场的收益为
2*C[i]*(win[i]+j)-2*D[i]*(lose[i]+rem[i]-j)+C[i]+D[i]
这个是单调增的,也只有单调增时才能拆边
因为我们这时不会先得到后一场的收益,再得到前一场的收益
所以从每个队向T连0-rem[i]这rem[i]+1条边
每条边的费用就是这场比赛能带来的收益
剪刀石头布就难想一些
首先是补集转化
总共有C(n,3)个三元组
一个三元组中,如果有一个点赢了两场,那么就不是“剪刀石头布”
所以答案就是C(n,3)-ΣC(win[i],2)
展开:C(n,3)-(win[i])*(win[i]-1)/2
同样建图,展开,发现后面部分每赢一场能得到的收益为增,这里是求最小费用,所以也一定会按场次顺序选择边流过
bzoj1449:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int maxn=10010,maxm=200010,inf=1061109567; using namespace std; int n,m,win[maxn],los[maxn],C[maxn],D[maxn],rem[maxn],mincost,maxflow,sum; struct data{int x,y;}G[maxn]; inline int game(int x){return x;} inline int team(int x){return m+x;} struct zkw_flow{ int pre[maxm],now[maxn],son[maxm],val[maxm],cost[maxm],tot,dis[maxn],S,T,q[maxn+10],head,tail;bool bo[maxn]; void add(int a,int b,int c,int d){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=c,cost[tot]=d;} void ins(int a,int b,int c,int d){add(a,b,c,d),add(b,a,0,-d);} void init(){memset(now,0,sizeof(now)),tot=1;} void spfa(){ memset(dis,63,sizeof(dis)); memset(bo,0,sizeof(bo)); q[tail=1]=S,dis[S]=head=0,bo[S]=1; while (head!=tail){ if (++head>maxn) head=1; int x=q[head]; for (int y=now[x];y;y=pre[y]){ if (val[y]>0&&dis[son[y]]>dis[x]+cost[y]){ dis[son[y]]=dis[x]+cost[y]; if (!bo[son[y]]){ if (++tail>maxn) tail=1; q[tail]=son[y],bo[son[y]]=1; } } } bo[x]=0; } } bool relabel(){ int mins=inf; for (int i=S;i<=T;i++) if (bo[i]) for (int y=now[i];y;y=pre[y]) if (!bo[son[y]]&&val[y]) mins=min(mins,dis[i]+cost[y]-dis[son[y]]); if (mins==inf) return 0; for (int i=S;i<=T;i++) if (!bo[i]) dis[i]+=mins; return 1; } int find(int x,int low,int cos){ if (x==T){mincost+=low*cos;return low;} bo[x]=1;int res=0; for (int y=now[x];y&&low;y=pre[y]){ if (!val[y]||bo[son[y]]||dis[son[y]]!=dis[x]+cost[y]) continue; int tmp=find(son[y],min(low,val[y]),cos+cost[y]); res+=tmp,low-=tmp,val[y]-=tmp,val[y^1]+=tmp; } return res; } void work(){ spfa();int aug=0; do{ do{ memset(bo,0,sizeof(bo)); aug=find(S,inf,0),maxflow+=aug; }while (aug); }while (relabel()); } }F; int main(){ scanf("%d%d",&n,&m),F.S=0,F.T=m+n+1;F.init(); for (int i=1;i<=n;i++) scanf("%d%d%d%d",&win[i],&los[i],&C[i],&D[i]); for (int i=1,x,y;i<=m;i++) scanf("%d%d",&x,&y),G[i]=(data){x,y},rem[x]++,rem[y]++; for (int i=1;i<=n;i++) sum+=win[i]*win[i]*C[i]+(los[i]+rem[i])*(los[i]+rem[i])*D[i]; for (int i=1;i<=m;i++) F.ins(F.S,game(i),1,0),F.ins(game(i),team(G[i].x),1,0),F.ins(game(i),team(G[i].y),1,0); for (int i=1;i<=n;i++) for (int j=0;j<=rem[i];j++) F.ins(team(i),F.T,1,2*(win[i]+j)*C[i]-2*(los[i]+rem[i]-j)*D[i]+C[i]+D[i]); F.work(),printf("%d\n",sum+mincost); return 0; <span style="font-size:14px;">}</span>
bzoj2597
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int maxn=15010,maxm=60010,inf=1061109567; using namespace std; int n,g[105][105],win[105],sum,maxflow,totcost; inline int team(int x){return x+n*n;} inline int game(int x,int y){return (x-1)*n+y;} struct zkw_flow{ int pre[maxm],now[maxn],son[maxm],val[maxm],cost[maxm],dis[maxn],tot,S,T;bool bo[maxn]; void add(int a,int b,int c,int d){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=c,cost[tot]=d;} void ins(int a,int b,int c,int d){add(a,b,c,d),add(b,a,0,-d);} void init(){memset(now,0,sizeof(now)),tot=1;} bool relabel(){ int mins=inf; for (int i=S;i<=T;i++) if (bo[i]) for (int y=now[i];y;y=pre[y]) if (val[y]&&!bo[son[y]]) mins=min(mins,dis[i]+cost[y]-dis[son[y]]); if (mins==inf) return 0; for (int i=S;i<=T;i++) if (!bo[i]) dis[i]+=mins; return 1; } int find(int x,int low,int cos){ if (x==T){totcost+=cos*low;return low;} bo[x]=1;int res=0; for (int y=now[x];y;y=pre[y]){ if (bo[son[y]]||!val[y]||dis[x]+cost[y]!=dis[son[y]]) continue; int tmp=find(son[y],min(low,val[y]),cos+cost[y]); res+=tmp,low-=tmp,val[y]-=tmp,val[y^1]+=tmp; if (!low) break; } return res; } void work(){ int aug=0;memset(dis,0,sizeof(dis)),maxflow=totcost=0; do{ do{ memset(bo,0,sizeof(bo)); aug=find(S,inf,0),maxflow+=aug; }while (aug); }while (relabel()); } void print(){ for (int i=game(1,1);i<=game(n,n);i++) if (now[i]){ int y1=now[i],y2=pre[y1],v1=son[y1]-n*n,v2=son[y2]-n*n; if (!val[y1]) g[v1][v2]=1,g[v2][v1]=0; else g[v2][v1]=1,g[v1][v2]=0; } } }F; int main(){ scanf("%d",&n),F.init(),F.S=0,F.T=team(n)+1; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) scanf("%d",&g[i][j]),win[i]+=(g[i][j]==1); for (int i=1;i<=n;i++) sum+=win[i]*(win[i]-1)/2; for (int i=1;i<=n;i++) for (int j=win[i];j<=n;j++) F.ins(team(i),F.T,1,j); for (int i=1;i<=n;i++) for (int j=i+1;j<=n;j++) if (g[i][j]==2) F.ins(F.S,game(i,j),1,0),F.ins(game(i,j),team(i),1,0),F.ins(game(i,j),team(j),1,0); F.work(),F.print(); printf("%d\n",n*(n-1)*(n-2)/6-sum-totcost); for (int i=1;i<=n;i++,puts("")) for (int j=1;j<=n;j++) printf("%d ",g[i][j]); return 0; }