首页 > 代码库 > UVALive 4255 Guess
UVALive 4255 Guess
这道题目让我学到了很多
一道不走寻常路的题目,给定一串数列的 和 的正负号,即假设数列 为 a1,a2,a3.....an,则一致了 Sij的正负号或者是否等于0,Sij代表了 从 ai 到 aj的和。要你求出任意一种序列,满足题目给定的Sij条件
确实是看了白书上的思路都还不太知道怎么写,然后看了LRJ的代码才知道原来还可以如此实现
很容易联系到前缀和sum,也就是说我们现在 得到了 sum[i]-sum[j] (j<i) 的正负号情况,要求满足情况的数列,那我们如果能得到满足情况的 sum[],那数列的每个数岂不就是
sum[i]-sum[i-1],那就是说我们得到合理的sum值,就相当于得到了最终结果
LRJ的思路是用拓补排序,。。。哎,以前写过裸的拓补排序,但是一到这种关键题目,就联想不起来可以这样搞,即,我们根据已经得到的sum[i]-sum[j]的正负号,得到 sum[i]和sum[j]的相对大小关系,当然还有个sum[0]=0作为对照,根据大小关系来进行建边,则,进行一次拓补排序就可以轻松把sum进行排序,其实说到底,只要sum排好了序,问题就解了,前面所有的都是为这个做铺垫,。。。既然把sum从小到大 或者 从大到小 排了序,就手动 把 1-n负责给他们呗,然后他们一相减就是合理的数列值了
题目里面有0,就代表某两个前缀和相等,在代码里用了 并查集合并 进行优化,不仅节省时间,还省去了处理相等的情况。
确实学到了好多,本来有点无所下手,这道题目首先,根据题目给的条件联想,要得到最终结果,其实是先得到前缀和的结果,因为题目给的就是和之间的大小关系,肯定要联想到前缀和的,不然不可能直接得到结果。其次拓补排序这种很实用的算法,也要懂得利用起来
其实我刚刚说了,所有处理的目的就是给 前缀和进行排序,前缀和顺序出来了,结果就出来了,所以也有人直接在读入的时候,读到+就对前面的sum--,读到+就对后面的--,这样sum的相对大小也可以出来。尽管没证明,好像这样做也可以,因为在扫第一行的时候,就把 sum1和其他sum的相对大小关系求出来了,然后扫第二行,就把sum2和除sum1以外的大小也求出来了,以此类推。。。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; int n; char ch[60],S[15][15]; int f[20]; int findset(int x){ if (x!=f[x]){ return f[x]=findset(f[x]); } return x; } int G[15][15]; int c[15],cnt,topo[15]; bool dfs(int u) { c[u]=-1; for (int v=0;v<=n;v++) if (G[u][v]){ if (c[v]<0) return false; else if (!c[v] && !dfs(v)) return false; } c[u]=1; topo[cnt--]=u; return true; } bool toposort() { cnt=n; memset(c,0,sizeof c); for (int i=0;i<=n;i++) if (!c[i]) if (!dfs(i)) return false; return true; } int sum[15]; int main() { int t; scanf("%d",&t); while (t--){ scanf("%d%s",&n,ch); for (int i=0;i<=n;i++) f[i]=i; int tmp=0; for (int i=1;i<=n;i++) for (int j=i;j<=n;j++){ S[i][j]=ch[tmp++]; if (S[i][j]==‘0‘){ f[findset(j)]=i-1; } } memset(G,0,sizeof G); for (int i=1;i<=n;i++) for (int j=i;j<=n;j++){ if (S[i][j]==‘-‘) G[findset(j)][findset(i-1)]=1; else if (S[i][j]==‘+‘) G[findset(i-1)][findset(j)]=1; } toposort(); //for (int i=0;i<=n;i++) cout<<topo[i]<<endl; for (int i=0;i<=n;i++) sum[topo[i]]=i; for (int i=1;i<=n;i++){ sum[i]=sum[f[i]]; if (i>1) printf(" "); printf("%d",sum[i]-sum[i-1]); } printf("\n"); } return 0; }