Summer training(一)

map

Most crossword puzzle fans are used to anagrams — groups of words with the same letters in different orders — for example OPTS, SPOT, STOP, POTS and POST. Some words however do not have this attribute, no matter how you rearrange their letters, you cannot form another word. Such words are called ananagrams, an example is QUIZ.

Obviously such definitions depend on the domain within which we are working; you might think that ATHENE is an ananagram, whereas any chemist would quickly produce ETHANE. One possible domain would be the entire English language, but this could lead to some problems. One could restrict the domain to, say, Music, in which case SCALE becomes a relative ananagram (LACES is not in the same domain) but NOTE is not since it can produce TONE.

Write a program that will read in the dictionary of a restricted domain and determine the relative ananagrams. Note that single letter words are, ipso facto, relative ananagrams since they cannot be “rearranged” at all. The dictionary will contain no more than 1000 words.

Input

Input will consist of a series of lines. No line will be more than 80 characters long, but may contain any number of words. Words consist of up to 20 upper and/or lower case letters, and will not be broken across lines. Spaces may appear freely around words, and at least one space separates multiple words on the same line. Note that words that contain the same letters but of differing case are considered to be anagrams of each other, thus ‘tIeD’ and ‘EdiT’ are anagrams. The file will be terminated by a line consisting of a single ‘#’.

Output

Outputwillconsistofaseriesoflines. Eachlinewillconsistofasinglewordthatisarelativeananagram intheinputdictionary. Wordsmustbeoutputinlexicographic(case-sensitive)order. Therewillalways be at least one relative ananagram. Sample Input ladder came tape soon leader acme RIDE lone Dreis peat ScAlE orb eye Rides dealer NotE derail LaCeS drIed NotE derail LaCeS drIed noel dire Disk mace Rob dries #

Sample Output

Disk

NotE

derail

drIed

eye

ladder

soon

题意:把每个单词都变成小写,然后排序,放到map中统计,如果没有重复的,按序输出原单词

#include<bits/stdc++.h>
using namespace std;
map<string ,int>cnt;
vector<string>words;
string bian(string s)//将大写变成小写,并且排序 
{
    string ans=s;
    for(int i=0;i<ans.length();i++)
       ans[i]=tolower(ans[i]);
    sort(ans.begin(),ans.end());
    return ans;
}
int main()
{
    string s;
    while(cin>>s)
    {
        if(s[0]=='#')break;
        words.push_back(s);
        string r=bian(s);
        if(!cnt.count(r)) 
            cnt[r]=0;
        cnt[r]++;
    }
    vector<string>ans;//将满足条件的单词放入ans数组中 
    for(int i=0;i<words.size();i++)
    {
        if(cnt[bian(words[i])]==1)
           ans.push_back(words[i]);
        sort(ans.begin(),ans.end());
    }
    for(int i=0;i<ans.size();i++)
    {
        cout<<ans[i]<<endl;
    }
    return 0;
 }  

 set

XY学长刚刚立下了再不过CET就直播xx的flag,为了不真的开启直播模式,XY学长决定好好学习英语。于是他每天都读一篇只包含生词的英语文章,并以自己高达450的智商在一秒钟之内记忆下来。

现在给你一篇XY学长今天要读的文章,请你写一个程序,输出他都学习到了哪些单词。
要求:如果文章中有相同的单词,那么仅仅输出一次;而且如果两个单词只有大小写不同,将他们视为相同的单词。Input测试数据将输入一篇文章。不超过5000行,每一行最多200个字符,并以EOF结束。Output按照字典序输出他学到的单词,每行输出一个单词,输出单词时所有的字母全部小写。
数据保证最多有5000个需要输出的单词。Sample Input样例输入①

a a a a a a a a, a a a a a a.
a a a a b a a a. a? a!!!

样例输入②

Adventures in Disneyland

Two blondes were going to Disneyland when they came to a fork in the
road. The sign read: "Disneyland Left."

So they went home.

Sample Output样例输出①

a
b

样例输出②

a
adventures
blondes
came
disneyland
fork
going
home
in
left
read
road
sign
so
the
they
to
two
went
were
when

Hint输入可能包含标点符号,但标点符号显然不能算作单词的一部分。

直接用set就可以;

#include<bits/stdc++.h>
using namespace std;
set<string>cun;
string word;
int main()
{
    while(cin>>word)
    {
        string k="";
        for(int i=0;i<word.length();i++)
        {
            if(isalpha(word[i]))//isalpha是判断是否是字母的函数,返回值是bool值
              k+=tolower(word[i]);
            else if(k!="")
            {
                cun.insert(k);
                k="";
            } 
        }
        if(k!="")//标点 
          cun.insert(k);
    }
    set<string>::iterator n;
    for(n=cun.begin();n!=cun.end();n++)
       cout<<*n<<endl;
    return 0;
 } 

输出用set的迭代器输出;

全排列函数

给你N个整数,分别是1,2,3,。。。N。问你全排列的第M个排列为多少?

Input输入包含几个测试用例。每个测试用例由两个数字组成,N和M(1<=N<=1000, 1<=M<=10000). 你可能会假设总有一个序列满足题目需求。输入在文件结束时终止。Output对于每个测试用例,您只需要输出满足题目要求的序列。输出序列时,应该在两个数字之间打印空格,但不要在最后一个数字之后输出任何空格。Sample Input

6 4
11 8

Sample Output

1 2 3 5 6 4
1 2 3 4 5 6 7 9 8 11 10

题解:这道题很简单,用一个全排列函数就可以了
next_permutation(全排列函数)
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=1e3+10;
int a[maxn];
int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        int x=0;
        memset(a,0,sizeof(a));
        for(int i=0;i<n;i++)
          a[i]=i+1;
        while(next_permutation(a,a+n))
        {
            x++;
            if(x==m-1)
            {
                cout<<a[0];
                for(int i=1;i<n;i++)
                   cout<<" "<<a[i];
                cout<<endl;
                break;
            }
        }
    }
    return 0;

}

 vector的使用

在计算机科学中的很多地方都会使用简单,抽象的方法来做分析和实验验究。比如在早期的规划学和机器人学的人工智能研究就利用一个积木世界,让机械臂执行操作积木的任务。
在这个问题中,你将在确定的规则和约束条件下构建一个简单的积木世界。这不是让你来研究怎样达到某种状态,而是编写一个“机械臂程序”来响应有限的命令集。

问题

问题就是分析一系列的命令,告诉机械臂如何操纵放在一个平台上的积木。最初平台上有n个积木(编号由0到n - 1),对于任意的0 ≤ i < n - 1,积木bi都与bi + 1相临,图示如下:

机械臂操作积木的有效指令列举如下:
*move a onto b
a和b都是积木的编号,先将a和b上面所有的积木都放回原处,再将a放在b上。
*move a over b
a和b都是积木的编号,先将a上面所有的积木放回原处,再将a放在b上。(b上原有积木不动)
*pile a onto b
a和b都是积木的编号,将a和其上面所有的积极组成的一摞整体移动到b上。在移动前要先将b上面所有的积极都放回原处。移动的一摞积木要保持原来的顺序不变。
*pile a over b
a和b都是积木的编号,将a和其上面所有的积极组成的一摞整体移动到b所在一摞积木的最上面一个积木上。移动的一摞积木要保持原来的顺序不变。
*quit
结束积木世界的操纵。
当a = b或a和b处在同一摞时,任何企图操作a和b的命令都是非法的。所有非法的命令都要忽略,且不能对当前积木的状态产生作用。

输入

输入由1个整数n开始开始,该整数独占一行,表示积木世界中的积木数量。你可以假定0 < n < 25。
从积木数量值的下一行开始是一系列的命令,每条命令独占一行。你的程序要处理所有的命令直到输入退出命令。
你可以假定所有的命令都按上文所示的格式给出。不会出现语法错误的命令。

输出

以积木世界的最终状态作为输出。每一个原始积木的位置i(0 ≤ i < n,n为积木数量)后面都要紧跟一个冒号。如果至少有一个积木在该位置上,冒号后面都要紧跟一个空格,然后是该位置上所有积木编号的序列。每2个积木的编号之间以一个空格隔开。行尾不能出现多余的空格。
每个积木位置独占一行(即第一行输入的n,对应输出n行数据)。

样例输入

10
move 9 onto 1
move 8 over 1
move 7 over 1
move 6 over 1
pile 8 over 6
pile 8 over 5
move 2 over 1
move 4 over 9
quit

样例输出

0: 0
1: 1 9 2 4
2:
3: 3
4:
5: 5 8 7 6
6:
7:
8:
9:
这道题如果将四个指令分开写,有些麻烦,所以应该找共同点
如move指令,a是全部归位的
onto指令,b是全部归位的
并且所有指令都是要把a放到b上,所以先考虑归位,再往上放就可以直接写代码啦
关于引用:

引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。

  引用的声明方法:类型标识符 &引用名=目标变量名;

 【例】:int a; int &ra=a; //定义引用ra,它是变量a的引用,即别名

#include<bits/stdc++.h>
using namespace std; 
const int maxn=30;
int n;
vector<int>pile[maxn];
//找木块a所在的pile和height
void find_block(int a,int& p,int& h)
{
    for(p=0;p<n;p++)
    {
        for(h=0;h<pile[p].size();h++)
        {
            if(pile[p][h]==a) 
                return;
        }
    }
}

//把p堆高度为h的木块上方的所有木块移回原位
void clear_above(int p,int h)
{
    for(int i=h+1;i<pile[p].size();i++)
    {
        int b=pile[p][i];
        pile[b].push_back(b);
    }
    pile[p].resize(h+1);
}

//把p堆高度为h及其上方的木块整体移动到p2堆的顶部
void pile_onto(int p,int h,int p2)
{
    for(int i=h;i<pile[p].size();i++)
    {
        pile[p2].push_back(pile[p][i]);
    }
    pile[p].resize(h);
}


int main()
{
    int a,b;
    string s1,s2;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        pile[i].push_back(i);
    }
    while(1)
    {
        cin>>s1;
        if(s1=="quit")  break;
        cin>>a>>s2>>b;
        int pa,pb,ha,hb;
        find_block(a,pa,ha);
        find_block(b,pb,hb);
        if(pa==pb)continue;
        if(s2=="onto")
        clear_above(pb,hb);
        if(s1=="move")
        clear_above(pa,ha);
        pile_onto(pa,ha,pb);
    }
    //输出 
    for(int i=0;i<n;i++)
    {
        cout<<i<<":";
        for(int j=0;j<pile[i].size();j++)
        cout<<" "<<pile[i][j];
        cout<<endl;
    }
    return 0;
 }

(这道题看了紫皮书)

queue的使用

有t个团队的人正在排一个长队。每次新来一个人时,如果他有队友在排队,那么这个新人会插队到最后一个队友身后。如果没有任何一个队友排队,则他会排到长队的队尾。输入每个团队中所有队员的编号,要求支持如下3种指令(前两种指令可以穿插进行)。
ENQUEUE:编号为X的人进入长队。
DEQUEUE:长队队首出队。
STOP:停止模拟。
对于每个DEQUEUE指令,输出出队的人的编号。

输入

输入文件中有一组或多组测试数据。
每组测试数据开始有t个团队。下面t行,每行的第一个数字代表这个团队人数,后面是这几个人的编号。编号为0到999999之间的一个整数。
每组测试数据以“STOP”结束。
输入以t==0时结束。
警告:一个测试用例可能包含最多200000(二十万)个命令,所以实现
团队的队列应该是有效的。

输出

对于每组测试数据,先打印一句"Scenario #k",k是第几组数据。对于每一个"DEQUEUE"指令,输出一个出队的人的编号。每组测试数据后要换行,即使是最后一组测试数据。

样例输入

2
3 101 102 103
3 201 202 203
ENQUEUE 101
ENQUEUE 201
ENQUEUE 102
ENQUEUE 202
ENQUEUE 103
ENQUEUE 203
DEQUEUE
DEQUEUE
DEQUEUE
DEQUEUE
DEQUEUE
DEQUEUE
STOP
2
5 259001 259002 259003 259004 259005
6 260001 260002 260003 260004 260005 260006
ENQUEUE 259001
ENQUEUE 260001
ENQUEUE 259002
ENQUEUE 259003
ENQUEUE 259004
ENQUEUE 259005
DEQUEUE
DEQUEUE
ENQUEUE 260002
ENQUEUE 260003
DEQUEUE
DEQUEUE
DEQUEUE
DEQUEUE
STOP
0

样例输出

Scenario #1
101
102
103
201
202
203

Scenario #2
259001
259002
259003
259004
259005
260001


这道题其实很好理解的,就是有两种队列,一种是每个团队的队列,一种就是总的队列
先用map,将每个队的队员放到它所在的队伍里,然后根据指令模拟
S---停止
D---队首出列,相对应的,队首元素所在的那一队的成员都出列,按顺序输出
E---插入,
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+10;
int main()
{
    int t;//t代表队伍数 
    int ca=1;
    int n,x;//n队员数,x队员 
    while(cin>>t&&t)
    {
        printf("Scenario #%d
",ca++);
        map<int,int>team;//记录团队编号
        for(int i=0;i<t;i++)
        {
            cin>>n;
            while(n--)
            {
                cin>>x;
                team[x]=i;
            } 
        } 
        //执行口令
        queue<int>a,b[maxn];//a是总队的队列,b[i]是团队i的队列 
        for(;;)
        {
            string s;
            int k;
            cin>>s;
            if(s[0]=='S') break;
            else if(s[0]=='D')
            {
                int q=a.front();
                cout<<b[q].front()<<endl;//队首出列 
                b[q].pop();
                if(b[q].empty())//队q全部出列 
                  a.pop();
            }
            else if(s[0]=='E')
            {
                cin>>k;
                int g=team[k];
                if(b[g].empty())
                   a.push(g);
                b[g].push(k);
            } 
        }
        cout<<endl;
    }
    return 0;
}

优先队列的使用

丑数是指不能被2,3,5以外的其他素数整除的数。把丑数从小到大排列起来,结果如下:
1,2,3,4,5,6,8,9,10,12,15……
求第1500个丑数

输入

没有输入

输出

The 1500'th ugly number is <number>.

最小的丑数是1,对于任意丑数x,2x,3x,5x都是丑数,将每次生成的丑数放到优先队列里,
从小到大,每次选出最小的丑数,继续生成新的丑数,用set是为了防止生成重复的丑数
#include<vector>
#include<set>
#include<queue>
#include<iostream>
using namespace std;
typedef long long ll;
set<ll>st;
const int ca[3]={2,3,5};
int main()
{
    priority_queue<int,vector<ll>,greater<ll> >qu;
    qu.push(1);
    st.insert(1);
    for(int i=1;;i++)
    {
        ll x=qu.top();
        qu.pop();
        if(i==1500)
        {
            cout<<"The 1500'th ugly number is "<<x<<".
";
            break;
        }
        for(int j=0;j<3;j++)
        {
            ll x2=x*ca[j];
            if(!st.count(x2))
            {
                st.insert(x2);
                qu.push(x2);
            }
        }
    }
    return 0;
 } 

优先队列:急诊病人插队,是考虑优先级的

priority_queue<int>pq;(越小的整数优先级越低)

个位数大的,优先级小的整数(priority_queue<int,vector<int>,cmp>pq)

struct cmp

{

bool operator()const int a,const int b)const{//a的优先级比b小时返回true

  return a%10>b%10;}};

越小的整数优先级越大(从小到大)

priority_queue<int,vector<int>,greater<int> >pq;

从大到小 less

拓扑排序

有N个比赛队(1<=N<=500),编号依次为1,2,3,。。。。,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即P1赢P2,用P1,P2表示,排名时P1在P2之前。现在请你编程序确定排名。

Input输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示队伍的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即P1队赢了P2队。
Output给出一个符合要求的排名。输出时队伍号之间有空格,最后一名后面没有空格。

其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。
Sample Input

4 3
1 2
2 3
4 3

Sample Output

1 2 4 3

拓扑序列是针对有向无环图的一种顺序的链接关系,其主要操作就是执行以下两个步骤知道没有入度为0的值

(1) 选择一个入度为0的顶点并输出之;
(2) 从网中删除此顶点及所有出边
#include<bits/stdc++.h>
using namespace std;
int n,m,a,b,mp[1000][1000];//邻接表中的关系
int in[1000];//入度
void tuopu()
{
    int i,j,top,k;
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            if(in[j]==0)//入度为0的点
            {
                in[j]--;
                printf("%d",j);
                if(i!=n)
                  printf(" ");
                else
                  printf("
");
                for(int k=1;k<=n;k++)
                   if(mp[j][k]==1)//与之有关系的点
                      in[k]--;
                break;
            }
        }
    }
}
int main()
{
    while(cin>>n>>m)
    {
        memset(in,0,sizeof(in));
        memset(mp,0,sizeof(mp));
        while(m--)
        {
            cin>>a>>b;
            if(mp[a][b]==0)
            {
                mp[a][b]=1;
                in[b]++;
            }
        }
        tuopu();
    }
    return 0;
}
ACM-DIY is a large QQ group where many excellent acmers get together. It is so harmonious that just like a big family. Every day,many "holy cows" like HH, hh, AC, ZT, lcc, BF, Qinz and so on chat on-line to exchange their ideas. When someone has questions, many warm-hearted cows like Lost will come to help. Then the one being helped will call Lost "master", and Lost will have a nice "prentice". By and by, there are many pairs of "master and prentice". But then problem occurs: there are too many masters and too many prentices, how can we know whether it is legal or not?

We all know a master can have many prentices and a prentice may have a lot of masters too, it's legal. Nevertheless,some cows are not so honest, they hold illegal relationship. Take HH and 3xian for instant, HH is 3xian's master and, at the same time, 3xian is HH's master,which is quite illegal! To avoid this,please help us to judge whether their relationship is legal or not.

Please note that the "master and prentice" relation is transitive. It means that if A is B's master ans B is C's master, then A is C's master.

InputThe input consists of several test cases. For each case, the first line contains two integers, N (members to be tested) and M (relationships to be tested)(2 <= N, M <= 100). Then M lines follow, each contains a pair of (x, y) which means x is y's master and y is x's prentice. The input is terminated by N = 0.
TO MAKE IT SIMPLE, we give every one a number (0, 1, 2,..., N-1). We use their numbers instead of their names.OutputFor each test case, print in one line the judgement of the messy relationship.
If it is legal, output "YES", otherwise "NO".Sample Input

3 2
0 1
1 2
2 2
0 1
1 0
0 0

Sample Output

YES
NO

这道拓扑序列的题是判断有没有环,和前面那道题思想是一样的,就是这道题有环,则不合法
 
//判断环 
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int in[110];//入度 
int mp[110][110];
int n,m; 
void tuopu()
{
    int flag=0;
    int i,j;
    for(i=0;i<n;i++)
    {    
      for(j=0;j<n;j++)
      {
          if(in[j]==0)//查找入度为0的点 
          {
              in[j]--;
              for(int k=0;k<n;k++)
              {
                  if(mp[j][k])//与之相连的边 
                    in[k]--;
            }
            break; 
        }
      }
      if(j==n)//没有入度为0的点,有环 
      {
          flag=1;
          break; 
      }
    } 
    if(flag)
       cout<<"NO"<<endl;
    else
       cout<<"YES"<<endl;    
} 
int main()
{
    int x,y;
    while(cin>>n>>m&&n&&m)
    {
        memset(in,0,sizeof(in));
        memset(mp,0,sizeof(mp));
        for(int i=0;i<m;i++)
        {
            cin>>x>>y;
            if(mp[x][y]==0)
            {
                mp[x][y]=1;
                in[y]++;
            }
        }
         tuopu();   
    }
} 
Dandelion's uncle is a boss of a factory. As the spring festival is coming , he wants to distribute rewards to his workers. Now he has a trouble about how to distribute the rewards.
The workers will compare their rewards ,and some one may have demands of the distributing of rewards ,just like a's reward should more than b's.Dandelion's unclue wants to fulfill all the demands, of course ,he wants to use the least money.Every work's reward will be at least 888 , because it's a lucky number.

InputOne line with two integers n and m ,stands for the number of works and the number of demands .(n<=10000,m<=20000)
then m lines ,each line contains two integers a and b ,stands for a's reward should be more than b's.OutputFor every case ,print the least money dandelion 's uncle needs to distribute .If it's impossible to fulfill all the works' demands ,print -1.Sample Input

2 1
1 2
2 2
1 2
2 1

Sample Output

1777
-1
这道题是关于老板发钱,怎么才能满足员工的需求,如第一组样例
2个人,一个人有要求
1的必须比2的多,每人888,有要求的人再+1,所以老板至少发888+889=1777
起初我看到这道题想到了判环,若有环,则不合理,老板不能满足所有人的需求,输出-1,
若无环,则每人至少888,n个人n*888,满足需要则在每个有需求的人的基础上+1,一个关于等差数列的前n项和
但是这样做会炸,就换一种思路,入度为0的员工工资最低
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int maxn=1e4+10;
vector<int>bian[maxn];
int gongzi[maxn];  
int in[maxn];  
queue<int> q;
int n,m;
void tuopu()
{
    for(int i=1;i<=n;i++)
    {
        if(in[i]==0)   //逆向建图,所以入度为0的工资最低
        {
          q.push(i);
          gongzi[i]=888; 
          in[i]--;
        }
    } 
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        for(int i=0;i<bian[x].size();i++)
        {
            in[bian[x][i]]--;
            if(in[bian[x][i]]==0)
            {
                q.push(bian[x][i]);
                gongzi[bian[x][i]]=gongzi[x]+1;
                in[bian[x][i]]--;
            }
        }
    }
    int flag=0;
    int ans=0;   //工资总数
    for(int i=1;i<=n;i++)
    {
        if(in[i]!=-1)flag++;  //有员工没有处理,说明存在回路,无法确定,要输出-1
    }
    if(flag!=0)printf("-1
");
    else
    {
        for(int i=1;i<=n;i++)
          ans+=gongzi[i];
        printf("%d
",ans);
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0;i<=n;i++)
         bian[i].clear();
        while(!q.empty())q.pop();
        memset(in,0,sizeof(in)); 
        memset(gongzi,0,sizeof(gongzi));
        for(int i=0;i<m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            in[a]++;
            bian[b].push_back(a);
        }
        tuopu();
    }
    return 0;
}

并查集

超市里有N个商品. 第i个商品必须在保质期(第di天)之前卖掉, 若卖掉可让超市获得pi的利润.
每天只能卖一个商品.
现在你要让超市获得最大的利润.

Input

多组数据.
每组数据第一行为一个整数N (0 <= N <= 10000), 即超市的商品数目
之后N行各有两个整数, 第i行为 pi, di (1 <= pi, di <= 10000)

Output

对于每一组数据, 输出当前条件下超市的最大利润

Sample Input

4
50 2
10 1
20 2
30 1

7
20 1
2 1
10 3
100 2
8 2
5 20
50 10

Sample Output

80
185

若想盈利多,先排序,从大的选,并查集将天数相同的放到同一个集合,

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e6+10;
int fa[maxn];
pair<int,int>a[maxn];
int fin(int x)//bingchaji
{
    if(fa[x]==x)
       return x;
    return fa[x]=fin(fa[x]);
}
int cmp(pair<int,int>p,pair<int,int>q)//paixu
{
    return p.first>q.first;
}
int main()
{
    int n;
    while(cin>>n)
    {
        for(int i=1;i<=n;i++)
            cin>>a[i].first>>a[i].second;
        for(int i=0;i<=maxn;i++)
           fa[i]=i;
        sort(a+1,a+1+n,cmp);
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            int t=fin(a[i].second);
            if(t==0)
               continue;
            else
            {
                fa[t]=fin(t-1);
                ans+=a[i].first;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}
鸡你太美病毒( Chicken you so beautiful), 是一种原因不明的流行性病毒。从最近一段时间起,学蔡徐坤打篮球成为新一代校园毒品,听到鸡你太美这首歌或者看到蔡徐坤打篮球的人很有可能就会感染这种病毒。由于它传染性很强( 只要你周围有人唱鸡你太美或者学蔡徐坤打篮球,你很有可能也会开始唱或者模仿,并开始劝你的同学一起唱或模仿)它开始被认为是全球威胁。为了减少传播给别人的机会, 最好的策略是隔离可能的患者。
学校里有许多学生团体。同一组的学生经常彼此相通,一个学生可以同时加入几个小组。为了防止鸡你太美病毒的传播, CCUTSOFT算法与程序设计协会收集了所有学生团体的成员名单。他们的标准操作程序如下:
一旦一组中有一个可能的患者, 组内的所有成员就都是可能的患者。
为了遏制这种病毒的传播,我们需要找到所有的患者。现在知道编号为0的小吉成员(感染源,因其有吉这个字,被蔡徐坤说太美,所以率先被感染)已经得了鸡你太美病毒,请你设计程序 发现所有可能的患者数量
 

Input

输入文件包含多组数据。
对于每组测试数据:
第一行为两个整数n和m, 其中n是学生的数量, m是团体的数量。0 < n <= 30000,0 <= m <= 500。
每个学生编号是一个0到n-1之间的整数,一开始只有0号的小吉被视为患者。
紧随其后的是团体的成员列表,每组一行。
每一行有一个整数k,代表成员数量。之后,有k个整数代表这个群体的学生。一行中的所有整数由至少一个空格隔开。
n = m = 0表示输入结束,不需要处理。

Output

对于每组测试数据, 一行输出一个正整数,即可能的患者数量。

Sample Input

100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2
200 2
1 5
5 1 2 3 4 5
1 0
0 0

Sample Output

4
1
1

将患病的放入一个集合,输出个数,模板题
#include<iostream>

using namespace std;
int q[30010];    
void init()
{
    for(int i=0;i<30010;i++)
        q[i]=i;
}

int find(int x)
{
    int h=x;
    if(h==q[h])
        return h;
    else
    {
        q[h]=find(q[h]); 
        return q[h];
    }
}

void unite(int x,int y)
{
    int k,f;
    k=find(x);
    f=find(y);
    if(k!=f)
    {
        if(k==0)
           q[f]=k;
        else
          q[k]=f;
    }
}

int main()
{
    int n,m,t,a,b,ans;
    while(cin>>n>>m)
    {
        if(n==0&&m==0) break;
        init();
        for(int i=0;i<m;i++)
        {
            cin>>t>>a;
            for(int j=1;j<t;j++)
            {
                cin>>b;
                unite(a,b);
            }    
        }
        ans=0;
        if(m==0)
           cout<<"1"<<endl;
        else
        {
            for(int i=0;i<n;i++)
            {
                if(!find(q[i]))
                   ans++;
            }
            cout<<ans<<endl;
        }    
    }
    return 0;
}
 
带权并查集
TT and FF are ... friends. Uh... very very good friends -________-b

FF is a bad boy, he is always wooing TT to play the following game with him. This is a very humdrum game. To begin with, TT should write down a sequence of integers-_-!!(bored).

Then, FF can choose a continuous subsequence from it(for example the subsequence from the third to the fifth integer inclusively). After that, FF will ask TT what the sum of the subsequence he chose is. The next, TT will answer FF's question. Then, FF can redo this process. In the end, FF must work out the entire sequence of integers.

Boring~~Boring~~a very very boring game!!! TT doesn't want to play with FF at all. To punish FF, she often tells FF the wrong answers on purpose.

The bad boy is not a fool man. FF detects some answers are incompatible. Of course, these contradictions make it difficult to calculate the sequence.

However, TT is a nice and lovely girl. She doesn't have the heart to be hard on FF. To save time, she guarantees that the answers are all right if there is no logical mistakes indeed.

What's more, if FF finds an answer to be wrong, he will ignore it when judging next answers.

But there will be so many questions that poor FF can't make sure whether the current answer is right or wrong in a moment. So he decides to write a program to help him with this matter. The program will receive a series of questions from FF together with the answers FF has received from TT. The aim of this program is to find how many answers are wrong. Only by ignoring the wrong answers can FF work out the entire sequence of integers. Poor FF has no time to do this job. And now he is asking for your help~(Why asking trouble for himself~~Bad boy)

InputLine 1: Two integers, N and M (1 <= N <= 200000, 1 <= M <= 40000). Means TT wrote N integers and FF asked her M questions.

Line 2..M+1: Line i+1 contains three integer: Ai, Bi and Si. Means TT answered FF that the sum from Ai to Bi is Si. It's guaranteed that 0 < Ai <= Bi <= N.

You can assume that any sum of subsequence is fit in 32-bit integer.
OutputA single line with a integer denotes how many answers are wrong.Sample Input

10 5
1 10 100
7 10 28
1 3 32
4 6 41
6 6 1

Sample Output

1

 说到带权并查集,就和压缩路经有关了

将每个节点直接与其Find()操作最终得到的节点链接,就是所谓的路径压缩

路径压缩过程中到同一个节点的和应该是相同的

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=2e5+10;
int fa[maxn],sum[maxn]; 
int init(int n)
{
    for(int i=0;i<=n;i++)
    {
        fa[i]=i;
        sum[i]=0;
    }
}

int find(int x)
{
   if(fa[x]!=x)
   {
       int t=fa[x];
       fa[x]=find(t);
       sum[x]+=sum[t];//更新权值
   }
   return fa[x];
}

int Union(int x,int y,int z)
{
    int fx=find(x);
    int fy=find(y);
    if(fx==fy)
    {
        return 1;
    }
    if(fx!=fy)
    {
        fa[fx]=fy;
        sum[fx]=sum[y]-sum[x]+z;
        return 0;
    }
    
}
int main()
{
    int n, m,ans,a,b,s;
    while(~scanf("%d %d", &n, &m))
    {
    ans=0;
    init(n);
    while(m--)
    {
        scanf("%d%d%d", &a, &b, &s);
        a--;
        if(Union(a,b,s)==1)
        {
            if(sum[a]-sum[b]!=s)
              ans++;
        }
    }
    cout<<ans<<endl;
  }
    return 0;
}

种类并查集

种类并查集总归一个思想,就是把一堆的东西分为一些种类,但实际上,每个东西的种类并不确定,强行给它确定一个种类的会不好处理,因为它本身的不确定性。但是如果把他们归到一个集合里,有一个共同的根节点,当要判断某两个东西的关系时,就可以根据他们跟根节点的关系判断

Background
Professor Hopper is researching the sexual behavior of a rare species of bugs. He assumes that they feature two different genders and that they only interact with bugs of the opposite gender. In his experiment, individual bugs and their interactions were easy to identify, because numbers were printed on their backs.
Problem
Given a list of bug interactions, decide whether the experiment supports his assumption of two genders with no homosexual bugs or if it contains some bug interactions that falsify it.

Input

The first line of the input contains the number of scenarios. Each scenario starts with one line giving the number of bugs (at least one, and up to 2000) and the number of interactions (up to 1000000) separated by a single space. In the following lines, each interaction is given in the form of two distinct bug numbers separated by a single space. Bugs are numbered consecutively starting from one.

Output

The output for every scenario is a line containing "Scenario #i:", where i is the number of the scenario starting at 1, followed by one line saying either "No suspicious bugs found!" if the experiment is consistent with his assumption about the bugs' sexual behavior, or "Suspicious bugs found!" if Professor Hopper's assumption is definitely wrong.

Sample Input

2
3 3
1 2
2 3
1 3
4 2
1 2
3 4

Sample Output

Scenario #1:
Suspicious bugs found!

Scenario #2:
No suspicious bugs found!

题意:每组所给的应该是异性,判断数据有没有出现同性的错误
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int yi[2222];                //记录昆虫的另一半
int F[2222];
int r[2222];
int find1(int n)             //找根节点
{
    int root=n;
    while(root!=F[root])
        root=F[root];
    return root;
}
void union1(int x,int y)    //将两个节点放在同一个集合里
{
    int root1=find1(x);
    int root2=find1(y);
    if(r[root1]>r[root2])
        F[root2]=root1;
    else
    {
        F[root1]=F[root2];
        if(r[root1]==r[root2])
            r[root1]++;
    }
}
int main()
{
    int t,n,m;
    scanf("%d",&t);
    for(int j=1; j<=t; j++)
    {
        int k=0;
        memset(yi,0,sizeof(yi));
        memset(r,0,sizeof(r));
        scanf("%d%d",&n,&m);
        for(int i=0; i<=n; i++)            //初始化
            F[i]=i;
        int a,b;
        while(m--)
        {
            scanf("%d%d",&a,&b);
            if(find1(a)==find1(b))         //如果根节点相同,则两昆虫在同一集合里,为同性
                k=1;
            else
            {
                if(yi[a]==0&&yi[b]==0)     //a,b在此前都没有对象
                {
                    yi[a]=b;               //a的对象赋值为b
                    yi[b]=a;               //b的对象赋值为a
                }
                else if(yi[a]==0)          //a此前没有对象,b有
                {
                    yi[a]=b;               //同上
                    union1(a,yi[b]);       //将a放在b的异性集合中
                }
                else if(yi[b]==0)          //b此前没对象,a有
                {
                    yi[b]=a;
                    union1(b,yi[a]);       //将b放在a的异性集合中
                }
                else                       //a,b都有对象
                {
                    union1(b,yi[a]);      
                    union1(a,yi[b]);       
                }
            }
        }
        printf("Scenario #%d:
",j);
        if(k==0)
            printf("No suspicious bugs found!
");
        else
            printf("Suspicious bugs found!
");
        cout << endl;
    }
    return 0;

}
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。

Input

第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。

Output

只有一个整数,表示假话的数目。

Sample Input

100 7
1 101 1 
2 1 2
2 2 3 
2 3 3 
1 1 3 
2 3 1 
1 5 5

Sample Output

3
这到题的物种有三种关系(环形食物链),即
同类re[x]=0
猎物re[x]=1.即fa[x]吃x
天敌re[x]=2.即x吃fa[x]
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=1e5;
int fa[maxn],re[maxn];
void init(int n)
{
    for(int i=0;i<=n;i++)
    {
        fa[i]=i;
    }
}
int find(int x)
{
    if(x!=fa[x])
    {
        int t=fa[x];
        fa[x]=find(t);
        re[x]=(re[x]+re[t])%3;
    }
    return fa[x];
}
int main()
{
    int n,m,d,a,b;
    scanf("%d%d",&n,&m);
    int cnt=0;
    init(n);
    memset(re,0,sizeof(re));
    for(int i=0;i<m;i++)
    {
        scanf("%d%d%d",&d,&a,&b);
        if(a>n||b>n||(d==2&&a==b))
        {
            cnt++;
            continue;
        }
        int fx=find(a);
        int fy=find(b);
        if(fx==fy)
        {
            if(d==1&&re[a]!=re[b])
              cnt++;
            if(d==2&&re[a]!=(re[b]+2)%3)
              cnt++;
        }
        else
        {
            fa[fy]=fx;
            re[fy]=(re[a]+(d-1)+(3-re[b]))%3;
        }
    }
    printf("%d
",cnt);
    return 0;
}
 

这套训练题将之前会的小部分又巩固了一遍,不会的也尝试学习了一些,也在b站看了一些相关的学习视频,有收获





原文地址:https://www.cnblogs.com/ylrwj/p/11192717.html