1. 程式人生 > >洛谷P2474 [SCOI2008]天平

洛谷P2474 [SCOI2008]天平

ron str txt aps -- spl sam data- con

P2474 [SCOI2008]天平

題目背景

2008四川NOI省選

題目描述

你有n個砝碼,均為1克,2克或者3克。你並不清楚每個砝碼的重量,但你知道其中一些砝碼重量的大小關系。你把其中兩個砝碼A 和B 放在天平的左邊,需要另外選出兩個砝碼放在天平的右邊。問:有多少種選法使得天平的左邊重(c1)、一樣重(c2)、右邊重(c3)?(只有結果保證惟一的選法才統計在內)

輸入輸出格式

輸入格式:

第一行包含三個正整數n,A,B(1<=A,B<=N,A 和B 不相等)。砝碼編號

為1~N。以下n行包含重量關系矩陣,其中第i行第j個字符為加號“+”表示砝

碼i比砝碼j重,減號“-”表示砝碼i比砝碼j 輕,等號“=”表示砝碼i和砝碼

j一樣重,問號“?”表示二者的關系未知。存在一種情況符合該矩陣。

輸出格式:

僅一行,包含三個整數,即c1,c2和c3。

輸入輸出樣例

輸入樣例#1: 復制
6 2 5
?+????
-?+???
?-????
????+?
???-?+
????-?
輸出樣例#1: 復制
1 4 1
輸入樣例#2: 復制
14 8 4
?+???++?????++
-??=?=???????=
??????????=???
?=??+?==??????
???-???-???-??
-=????????????
-??=???=?-+???
???=+?=???????
??????????????
??????+???????
??=???-????-??
????+?????+???
-?????????????
-=????????????
輸出樣例#2: 復制
18 12 11

說明

4<=n<=50

技術分享
/*
    w[i]<w[j]就連一條i->j的邊
    w[i]=w[j]就連一條雙向邊
    跑Tarjan把圖縮成有向無環圖
    最後入度為0的點的質量就是1,按拓排給其他的賦值為2,3
    由於少考慮了一些情況,導致不合法的情況也會統計到答案中,所以炸了 
*/
#include<iostream>
#include<cstdio>
#include<queue>
#define maxn 51
using
namespace std; int n,A,B,belong[maxn],wei[maxn],w[maxn],cnt,map[maxn][maxn],du[maxn]; int num,head[maxn],dfn[maxn],low[maxn],st[maxn],top,g,gr[maxn][maxn]; int Num,Head[maxn]; double vis[maxn]; struct node{ int to,pre; }e[maxn*2],E[maxn*2]; char s[maxn]; void Insert(int from,int to){ e[++num].to=to; e[num].pre=head[from]; head[from]=num; } void Insert2(int from,int to){ E[++Num].to=to; E[Num].pre=Head[from]; Head[from]=Num; } void Tarjan(int u){ cnt++; dfn[u]=low[u]=cnt;st[++top]=u;vis[u]=1; for(int i=head[u];i;i=e[i].pre){ int v=e[i].to; if(!dfn[v]){ Tarjan(v); low[u]=min(low[u],low[v]); } else if(vis[v])low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]){ g++; while(st[top]!=u){ int x=st[top]; top--;vis[x]=0; gr[g][++gr[g][0]]=x; belong[x]=g; } belong[u]=g; gr[g][++gr[g][0]]=u; vis[u]=0; top--; } } queue<int>q; int main(){ freopen("Cola.txt","r",stdin); scanf("%d%d%d",&n,&A,&B); for(int i=1;i<=n;i++){ scanf("%s",s+1); for(int j=1;j<=n;j++){ if(s[j]==-)map[i][j]=-1,map[j][i]=1; if(s[j]==+)map[i][j]=1,map[j][i]=-1; if(s[j]===)map[i][j]=2,map[j][i]=2; } } for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ if(map[i][j]==1)Insert(j,i);//i>j連一條從j指向i的邊 if(map[i][j]==-1)Insert(i,j);//i<j連一條從i指向j的邊 if(map[i][j]==2)Insert(i,j),Insert(j,i); } } for(int i=1;i<=n;i++)if(!dfn[i])Tarjan(i); for(int i=1;i<=g;i++){ for(int j=1;j<=gr[i][0];j++){ int now=gr[i][j]; for(int k=head[now];k;k=e[k].pre){ int to=e[k].to; if(belong[now]!=belong[to]) Insert2(belong[now],belong[to]),du[belong[to]]++; } } } for(int i=1;i<=g;i++){ if(du[i]==0)q.push(i),wei[i]=1; } while(!q.empty()){ int now=q.front();q.pop(); for(int i=Head[now];i;i=E[i].pre){ int to=E[i].to; du[to]--; if(du[to]==0){ wei[to]=wei[now]+1; q.push(to); } } } for(int i=1;i<=n;i++)w[i]=wei[belong[i]]; int c1=0,c2=0,c3=0,sum=w[A]+w[B]; for(int i=1;i<=n;i++){ if(i==A||i==B)continue; for(int j=i+1;j<=n;j++){ if(j==A||j==B)continue; if(w[i]+w[j]<sum)c1+=1; if(w[i]+w[j]==sum)c2+=1; if(w[i]+w[j]>sum)c3+=1; } } cout<<c1<< <<c2<< <<c3; }
20分 拓撲排序

洛谷P2474 [SCOI2008]天平