拓扑三小题

 

病毒(virus)

链接:http://ybt.ssoier.cn:8088/problem_show.php?pid=1396
时间限制: 1000 ms         内存限制: 65536 KB

【题目描述】

有一天,小y突然发现自己的计算机感染了一种病毒!还好,小y发现这种病毒很弱,只是会把文档中的所有字母替换成其它字母,但并不改变顺序,也不会增加和删除字母。

现在怎么恢复原来的文档呢!小y很聪明,他在其他没有感染病毒的机器上,生成了一个由若干单词构成的字典,字典中的单词是按照字母顺序排列的,他把这个文件拷贝到自己的机器里,故意让它感染上病毒,他想利用这个字典文件原来的有序性,找到病毒替换字母的规律,再用来恢复其它文档。

现在你的任务是:告诉你被病毒感染了的字典,要你恢复一个字母串。

【输入】

第一行为整数K(≤50000),表示字典中的单词个数。

以下K行,是被病毒感染了的字典,每行一个单词。

最后一行是需要你恢复的一串字母。

所有字母均为小写。

【输出】

输出仅一行,为恢复后的一串字母。当然也有可能出现字典不完整、甚至字典是错的情况,这时请输出一个0。

【输入样例】

6
cebdbac
cac
ecd
dca
aba
bac
cedab

【输出样例】

abcde
题解:给的数据是从a到某个字母,中间没有缺少字母
这是一道巨坑的题,有好几个盘错的地方:
1.比较大小时有环
2.有多个字母处于同一等级,无法判大小
3.出现过未出现的字母
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100005;
string s, a, q;
int m,n,r[maxn],Real[maxn];
vector<int>G[maxn];
string ans;
bool Find(int a,int b){
    for(int i = 0; i < G[a].size(); i++)
        if(G[a][i] == b)return true;
    return false;
}
void merge(int a, int b){
    G[a].push_back(b);
    r[b]++;
}
bool AOV(){
    stack <int> t;
    int tot = 0;
    for(int i = 0; i <= m; i++)
        if(!r[i])t.push(i);
    if(t.size() > 1)return false;
    while(!t.empty()){        
        int u = t.top();
        Real[u] = ++tot;
        t.pop();
        for(int i = 0; i < G[u].size(); i++)
            if((--r[G[u][i]]) == 0){
                t.push(G[u][i]);
            }
        if(t.size() > 1)return false;        
    }
    if(tot < m)return false;
    return true;
}
int main(){
    
    cin>>n;    
    cin>>s;    
    for(int i = 1; i < n; i++){
        cin>>a;    
        int flag = 0;
        for(int j = 0; j < s.size(); j++){            
            if(!flag && j < a.size() && a[j] != s[j]){
                flag = 1;
                if(!Find(s[j]-'a', a[j]-'a'))
                    merge(s[j]-'a', a[j]-'a');
                m = max(m, s[j] - 'a');
            }
            m = max(m, s[j] - 'a');
        }
        
        s = a;
    }
    for(int i = 0; i < a.size(); i++)m = max(m, a[i] - 'a');
    cin>>s;
    int e = 0;
    if(!AOV()){
        cout<<0<<endl;return 0;
    }
    else for(int i = 0; i < s.size(); i++)
            if(s[i] - 'a' > m){
                cout<<0<<endl;
                return 0;
            }
            else ans[++e] = Real[s[i] - 'a'] - 1 +'a';
            
    for(int i = 1; i <= e; i++)printf("%c",ans[i]);
    
}

【例4-13】奖金

时间限制: 1000 ms         内存限制: 65536 KB

【题目描述】

由于无敌的凡凡在2005年世界英俊帅气男总决选中胜出,Yali Company总经理Mr.Z心情好,决定给每位员工发奖金。公司决定以每个人本年在公司的贡献为标准来计算他们得到奖金的多少。

于是Mr.Z下令召开m方会谈。每位参加会谈的代表提出了自己的意见:“我认为员工a的奖金应该比b高!”Mr.Z决定要找出一种奖金方案,满足各位代表的意见,且同时使得总奖金数最少。每位员工奖金最少为100元。

【输入】

第一行两个整数n,m,表示员工总数和代表数;

以下m行,每行2个整数a,b,表示某个代表认为第a号员工奖金应该比第b号员工高。

 

【输出】

若无法找到合理方案,则输出“Poor Xed”;否则输出一个数表示最少总奖金。

【输入样例】

2 1
1 2

【输出样例】

201

【提示】

【数据规模】

80%的数据满足:n≤1000,m≤2000;

100%的数据满足:n≤10000,m≤20000。

题解:拓扑+贪心
#include<bits/stdc++.h>
using namespace std;
const int maxn = 10005, INF = 1000000008;
vector <int> G[maxn];
stack <int> t;
int r[maxn];
int N,M,money;
void work(){
    int tot = 0, q = 0;
    while(tot < N){
        for(int i = 1; i <= N; i++)
             if(!r[i])
             tot++,t.push(i),r[i] = INF;             
         if(t.empty()){
             money = -1;
             return ;
         }
         money += q*t.size();
         while(!t.empty()){
             int u = t.top();
             t.pop();
             //cout<<u<<"a"<<G[u].size();
             for(int i = 0; i < G[u].size(); i++){
                 r[G[u][i]]--;
             }
         }
         q++;
    }
     
 }

int main(){
    
    cin>>N>>M;
    for(int i = 1; i <= M; i++){
        int u,v;
        cin>>u>>v;
        G[v].push_back(u);
        r[u]++;
    }
    work();
    if(money == -1)cout<<"Poor Xed"<<endl;
    else cout<<money + 100*N<<endl;
}
 

烦人的幻灯片(slides)

时间限制: 1000 ms         内存限制: 65536 KB


【题目描述】


李教授将于今天下午作一次非常重要的演讲。不幸的事他不是一个非常爱整洁的人,他把自己演讲要用的幻灯片随便堆在了一起。因此,演讲之前他不得不去整理这些幻灯片。作为一个讲求效率的学者,他希望尽可能简单地完成它。教授这次演讲一共要用n张幻灯片(n≤26),这n张幻灯片按照演讲要使用的顺序已经用数字1~n编了号。因为幻灯片是透明的,所以我们不能一下子看清每一个数字所对应的幻灯片。


现在我们用大写字母A,B,C……再次把幻灯片依次编号。你的任务是编写一个程序,把幻灯片的数字编号和字母编号对应起来,显然这种对应应该是唯一的;若出现多种对应的情况或是某些数字编号和字母编号对应不起来,我们称对应是无法实现的。


【输入】


第一行只有一个整数n,表示有n张幻灯片,接下来的n行每行包括4个整数xmin,xmax,ymin,ymax(整数之间用空格分开)为幻灯片的坐标,这n张幻灯片按其在文件中出现的顺序从前到后依次编号为A,B,C……,再接下来的n行依次为n个数字编号的坐标x,y,显然在幻灯片之外是不会有数字的。


【输出】


若是对应可以实现,输出文件应该包括n行,每一行为一个字母和一个数字,中间以一个空格隔开,并且每行以字母的升序排列,注意输出的字母要大写并且定格;反之,若是对应无法实现,在文件的第一行顶格输出None即可。首行末无多余的空格。


【输入样例】


4
6 22 10 20
4 18 6 16
8 20 2 18
10 24 4 8
9 15
19 17
11 7
21 11

【输出样例】


A 4
B 1
C 2
D 3
题解:PPT有编号,但叠在一起,又透明,所以按顺序一个个排除编号,得到对应关系
#include<bits/stdc++.h>
using namespace std;

const int maxn = 50;
struct ppt{
    int xmin,xmax,ymin,ymax;
}p[maxn];
struct A{
    int num,name;
}ans[maxn];
vector <int> c[maxn];
int r[maxn];
stack <int> s;

bool cmp(A aa,A bb){
    return aa.name < bb.name;
}
bool check(int x,int y,int i){
    if(x<=p[i].xmax&&x>=p[i].xmin&&y<=p[i].ymax&&y>=p[i].ymin)
        return true;
    return false;
}
int main(){
    int N,tot=0;
    cin>>N;
    for(int i=1;i<=N;i++)
        cin>>p[i].xmin>>p[i].xmax>>p[i].ymin>>p[i].ymax;
    for(int i=1;i<=N;i++){
        int x,y;
        cin>>x>>y;
        for(int j=1;j<=N;j++){            
            if(check(x,y,j)){
                c[i].push_back(j);
                r[j]++;
            }
            
        }
    }    
    for(int i=1;i<=N;i++)
        if(r[i]==1)s.push(i);
    while(!s.empty() && tot<N){
        int tmp = s.top();
        s.pop();
        for(int i = 1;i <= N;i++){
            vector<int>::iterator it = find(c[i].begin(), c[i].end(), tmp);
            if(it != c[i].end()){
                q = i,ans[++tot].num=i,ans[tot].name=tmp;
                break;
            }
        }
        for(int i = 0;i < c[q].size();i++){
            r[c[q][i]]--;
            if(r[c[q][i]] == 1)s.push(c[q][i]);
        }
        c[q].clear();
    }
    if(tot < N)
        cout<<"None"<<endl;
    else {
        sort(ans + 1, ans +1+tot, cmp);
        for(int i = 1;i <= tot ;i++)
            printf("%c %d
",ans[i].name-1+'A',ans[i].num);
    }
//    system("pause");
    return 0;
} 

【例4-12】家谱树

链接:http://ybt.ssoier.cn:8088/problem_show.php?pid=1351
时间限制: 1000 ms         内存限制: 65536 KB


【题目描述】


有个人的家族很大,辈分关系很混乱,请你帮整理一下这种关系。


给出每个人的孩子的信息。


输出一个序列,使得每个人的后辈都比那个人后列出。


【输入】


第1行一个整数N(1≤N≤100),表示家族的人数;


接下来N行,第I行描述第I个人的儿子;


每行最后是0表示描述完毕。


 


【输出】


输出一个序列,使得每个人的后辈都比那个人后列出;


如果有多解输出任意一解。


 


【输入样例】


5
0
4 5 1 0
1 0
5 3 0
3 0

【输出样例】


2 4 5 3 1
#include<bits/stdc++.h>
using namespace std;

#define INF 10000008
const int maxn = 100000+5;
vector <int> G[maxn];
stack <int>t;
int r[maxn];

int main(){
    int n;
    cin>>n;
    for(int i = 1; i <= n; i++){
        int a;
        while(scanf("%d",&a) == 1)
            if(!a)break;
            else G[i].push_back(a), r[a]++;
        
    }
    do{
        for(int i = 1; i <= n; i++)
            if(!r[i])
                 t.push(i), r[i] = INF;
        if(t.empty())break;
        while(!t.empty()){
            int m = t.top();
            cout<<m<<" ";
            t.pop();
            for(int i = 0 ; i < G[m].size(); i++){
                int u = G[m][i];
                r[u]--;
            }
                
        }
        
    }while(1);
    
    
}
原文地址:https://www.cnblogs.com/EdSheeran/p/8467405.html