luanqibazao

#include <iostream>
#include <stdio.h>
#include <vector>
#include <algorithm>

//组合数模板
/*
int C(int n, int r) {
    int sum = 1;
    for (int i = 1; i <= r; i++) {
        sum = sum * (n + 1 - i) / i;
    }
    return sum;
}
 */
//----------------
//将字符串转换为数字
//sscanf("12345","%d",&v)
//字符串如果后面有字母,他会自动忽略,只用数字
//可以配合正则表达式使用,类似于%4d之类的

//------------
//反转字符串,将本身反转,并且存在本身
//cin >> A;
//reverse(A.begin(),A.end());
//cout << A << endl;
//---------------------------------------------------
/*
二分法
        今天做了两个二分的题目,感觉真的很神奇,现在把代码贴一下,方便以后查阅。

codeforces 779D

题目是给你一个操作串,然后给你一个待匹配串,接下来给你n个数字(n是操作串的长度),然后依次删除ai对应的操作串的下标,问你到第几个的时候,

操作串中没有待匹配串。

思路就是二分查找,查找什么呢?查找将前x个str[ai]删除,看是否满足条件,然后恢复,继续向左或者向右查找

        简单看了一下二分,关键部分有几个,处理mid的时候,有2种方式,第一种是处理整形时候的方式,第二种是处理浮点型数据的方式。

具体看代码。

#include <iostream>
#include <string>
using namespace std;
string aim,str;
const int Maxn = 230010;
int del[Maxn];
bool vis[Maxn];

bool ok( int l,int r ){
    bool flag = false;
    for( int i = 0; i <= r; i++ ){//这里要把mid前面全部清空
        vis[del[i]] = true;
    }
    int j = 0;
    for( int i = 0; i < str.length(); i++ ){
        if( !vis[i] and str[i] == aim[j] ){
            j++;
        }
    }
    if( j == aim.length() ){
        flag = true;
    }
    for( int i = 0; i <= r; i++ ){
        if( vis[del[i]] ){
            vis[del[i]] = false;
        }
    }
    return flag;
}

int main(){
    cin >> str >> aim;
    int len = str.length();
    for( int i = 0; i < len; i++ ){
        scanf("%d",del+i);
        del[i] -= 1;
    }
    int l = 0,r = len-1;
    int mid;
    while( l <= r ){
        mid = (l+r)/2;
        if( ok( l,mid ) ){
            l = mid+1;//重点
        }else{
            r = mid-1;
        }
    }
    cout << l << endl;
}
 */
//--------------------------------------
//最短路径算法
/*
最短路径算法
        Problem Description
        在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?

Input
        输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。

Output
        对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间

        Sample Input
2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0

Sample Output
3
2
因为用int WA了好久,可是又懒的把int全部替换成ll,宏定义一下嘞

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define int ll
const int Maxn = 200;
int Map[Maxn][Maxn];
int dis[Maxn];
bool vis[Maxn];
int n,m,a,b;

int Dijstra(int x){
    for(int i = 1; i <= n; i++){
        dis[i] = Map[x][i];
    }
    vis[x] = true;
    int minn = 2147483645;
    for(int i = 1; i <= n; i++){
        int u;
        minn = 2147483644;
        for(int j = 1; j <= n; j++){
            if(!vis[j] and dis[j] < minn){
                minn = dis[j];
                u = j;
            }
        }
        vis[u] = true;
        for(int j = 1; j <= n; j++){
            if(!vis[j] and dis[u] + Map[u][j] < dis[j] ){
                dis[j] = Map[u][j]+dis[u];
            }
        }
    }
    return dis[n];
}

main(){
    while(cin >> n >> m){
        if(n == 0 and m == 0 ){
            return 0;
        }
        for( int i = 0; i < Maxn; i++){
            for(int j = 0; j < Maxn; j++){
                Map[i][j] = 2147483647;
            }
        }
        memset(vis,false,sizeof(vis));
        for(int i = 0; i <= n; i++){
            dis[i] = 2147483646;
        }
        int a,b,c;
        for(int i = 0; i < m; ++i){
            cin >> a >> b >> c;
            Map[b][a] = c;
            Map[a][b] = c;
        }
        printf("%d\n",Dijstra(1));
    }
}

弗洛伊德算法

#include <iostream>
#include <cstdio>
using namespace std;
const int INF=0x3f3f3f3f;
int dis[110][110];
int main(){
    int i,j,k,n,m,p,q,s;
    while(scanf("%d%d",&n,&m)!=EOF,n+m){
        for (i=1;i<=n;i++){
            for(j=1;j<=n;j++){
                dis[i][j]=INF;
            }
        }
        for (i=0;i<m;i++){
            scanf("%d%d%d",&p,&q,&s);
            dis[p][q]=dis[q][p]=s;
        }
        for (k=1;k<=n;k++){
            for (i=1;i<=n;i++){
                for (j=1;j<=n;j++){
                    if (dis[i][j]>dis[i][k]+dis[k][j]){
                        dis[i][j]=dis[i][k]+dis[k][j];
                    }
                }
            }
        }
        printf("%d\n",dis[1][n]);
    }
    return 0;
}

 */

/*----------------------------最长公共子序列

 #include <bits/stdc++.h>
using namespace std;
const int Maxn = 1011;
int dp[Maxn][Maxn];
int main(){
    string A,B;
    cin >> A >> B;
    int a = A.length(),b = B.length();
    int maxn = -1;
    for( int i = 1; i <= A.length(); i++ ){
        for( int j = 1; j <= B.length(); j++ ){
            if( A[i-1] == B[j-1] ){
                dp[i][j] = dp[i-1][j-1]+1;
            }else if( dp[i][j-1] > dp[i-1][j] ){
                dp[i][j] = dp[i][j-1];
            }else{
                dp[i][j] = dp[i-1][j];
            }
            maxn = max( maxn,dp[i][j] );
        }
    }
    vector<char> v;
    for( int i = a,j = b; i >= 1 && j >= 1; ){
        if( A[i-1] == B[j-1] ){
            v.push_back(A[i-1]);
            i--,j--;
        }else{
            if( dp[i][j-1] > dp[i-1][j] ){
                j--;
            }else{
                i--;
            }
        }
    }
    reverse(v.begin(),v.end());
    for(auto x:v){
        cout << x;
    }
}
 */
/*威左夫问题:只要确定当前状态是否是必败态即可,输入m,n,a = min(m,n),b = max(m,n)

只要判断当前的状态是否是必败态,if int((b-a)*((1+sqrt(5.0))/2.0)) == a: 为真就是必败态(不需要保证保证大小,是按照对称轴对称来的)

bash博弈

判断当前的石子个数是否满足n = r*(m+1) + s,如果s为0,则必败,否则必胜

*/

/*-------快速幂
 *
ll quick_pow(ll x,ll n){
    ll res=1;
    while(n>0){
        if(n & 1){
            res=(res*x)%Max;
        }
        x=(x*x)%Max;
        n >>= 1;
    }
    return res;
}*/

/*
 HDU1166敌兵布阵

        这道题如果用常规暴力的做法,就把所有营地的士兵存在一个数组里面,然后对于每次操作直接更新对应位置的数,对于每次询问直接从i到j加起来。然而这么操作下来,对于极限数据50000个人,40000条命令,显然是会超时的,那么一种新的数据结构线段树就应运而生了。

        首先第一个疑问:为什么线段树会快?

        显然对于m个点n次询问,暴力的做法时间复杂度是O(m*n)的。然而线段树作为一棵二叉树,继承了二叉树O(logn)的优良品质,对于这道题最坏的复杂度也是O(m*logn)的,这个量显然是符合时间要求的。

        第二:线段树如何处理?

        倘若节点x(x为奇数)记录的是第1个点的数据,节点x+1记录的是第2个点的数据,那么节点x/2记录的就是区间[1,2]上的有效数据,以此类推,最顶端的父节点记录的就是区间[1,n]上的有效数据,那么对于每个点的数据,有且仅有logn个节点的数据会被它影响,因此每次更新只用更新logn个点,查询亦然,这样就有效地节约了时间。

        对于每个节点,其代表的是区间[x,y]之间的值,那么其左儿子节点代表的就是[x,(x+y)/2]区间的值,右儿子节点代表的是区间[(x+y)/2+1,y]上的值,既保证了无重复,又保证了树的层数最短,查询效率最高。

        第三:线段树的具体实现呢?

        那么我们就跟着刚才拿到题目来详细讲解。

复制代码
int tre[N*4];
void build(int num,int le,int ri)
{
    if(le==ri)
    {
        scanf("%d",&tre[num]);
        return ;
    }
    int mid=(le+ri)/2;
    build(num*2,le,mid);
    build(num*2+1,mid+1,ri);
    tre[num]=tre[num*2]+tre[num*2+1];
}
复制代码
 首先是建树,在这里num存的是下标,而le和ri表示的是这个区间的左右端点,那么每往下一层num*2,区间则折半,保证了最少的层数,而此时内存占用大约为4倍的点数,所以开数组的时候开tre[4*N]。这个题因为需要读入每个点,作为二叉树的先序遍历,很好地保证了第x个点正好读入在le=ri=x的那个tre[num]里面。而父亲节点所代表的区间包含了子节点所代表的区间,所以子节点的值又会影响父节点,因此每次建立完儿子节点之后,又会通过tre[num]=tre[num*2]+tre[num*2+1];操作将父亲节点初始化,当然此处为求和操作所以是+,不同的题可以选择取最值等不同运算符。当然不同的题根据需求可以采取对tre[num]赋值或者memset等方法来建树以及初始化。

复制代码
void update(int num,int le,int ri,int x,int y)
{
    if(le==ri)
    {
        tre[num]+=y;
        return ;
    }
    int mid=(le+ri)/2;
    if(x<=mid)
        update(num*2,le,mid,x,y);
    else
        update(num*2+1,mid+1,ri,x,y);
    tre[num]=tre[num*2]+tre[num*2+1];
}
复制代码
接下来是修改操作,继承了上面的num,le,ri,保证了一致性,同时此处做的是对于第x个点增加y个人的操作,所以寻找到x所对应的tre[num],然后操作,并回退。而此时需要注意的是,对于x操作了之后,所有包含x的区间的tre[num]都需要被修改,因此也就有了在回退前的tre[num]=tre[num*2]+tre[num*2+1];操作。而这个题操作的是增加减少(减少直接传-x),而其他的诸如取最大最小值、取异或值等等都只用对于对应的运算符做修改即可。

复制代码
int query(int num,int le,int ri,int x,int y)
{
    if(x<=le&&y>=ri)
    {
        return tre[num];
    }
    int mid=(le+ri)/2;
    int ans=0;
    if(x<=mid)
        ans+=query(num*2,le,mid,x,y);
    if(y>mid)
        ans+=query(num*2+1,mid+1,ri,x,y);
    return ans;
}
复制代码
最后是查询操作,依然继承了num,le,ri。而此处做的是区间查询,(其实如果x=y就成了单点查询)那么如果查询区间[x,y]包含了目前的区间[le,ri],即x<=le&&y>=ri,那么此时的tre[num]就已经是这一部分的有效数据了,所以直接return即可,否则继续分区间查询。同样,此时根据题意所做的求和操作可以对应替换为异或、取最值等操作。

        以上就是线段树最简单的功能——单点更新。

        下面为大家带来的线段树稍微难一点但是基本是最常用的一个用法:区间更新。

区间更新对于初学者来说是一个坎,其中有几步相对较难理解。但是只要掌握,就能解决绝大多数线段树的题目了。

        首先刚刚那个题每次是每个营地增减人,那么如果每次是x号营地到y号营地每次都增减人呢?这样我们就会发现单点更新操作不适用了,无论我们如何调整都无法达到效果,而且即使每次对于x到y之间每个营地都执行一次单点操作,结果上看似可以,但是极限情况下我们每次对于1到n号进行更新的话,复杂度就会达到O(m*n*logn),这样就绝对会超时了,那么怎么做呢?这里就要用到区间更新了。

        对于区间更新的,我们来看下面这个题:

HDU1556Color the ball

        这个题很显然满足刚刚所提到的每次对于其中的一段里面的所有点进行操作。

        那么既然是线段树,首先我们依然是建树初始化,在建树阶段区别不多,该读值的读值,该赋值的赋值,该置0的置0,这题根据需要,在最开始所有的球都是未被涂色的,那么直接所有tre[num]置为0即可,于是这一次我们就可以不必单独写一个build了,直接memset(tre,0,sizeof(tre));即可。

        但是与单点更新最大的不同就是:它多了一个lazy数组!!!!!!!!!!重要的地方要打10个感叹号。

        laz,全称lazy,中文叫懒惰标记或者延迟更新标记。

        因为我们知道,如果我们每次都把段更新到节点上,那么操作次数和每次对区间里面的每个点单点更新是完全一样的哇!那么怎么办呢?仔细观察线段树,你会发现一个非常神奇的地方:每个节点表示的值都是区间[le,ri]之间的值有木有!!!!!!!!!!为什么说它神奇呢?更新的区间的值,存的区间的值!简直就是天作之合,我每次更新到对应区间了我就放着,我等下次需要往下更新更小的区间的时候,再把两次的值一起更新下去有木有啊!可以节约非常多时间啊有木有啊!

       对,这就是laz[num]的作用。下面我们跟着题再来逐步感受。

       首先在最最最最最最开始,是没有进行过更新操作的,那么laz[num]自然是全部置为0(当然有的题有额外的初始化要求,大家根据题目自行定夺)。

       那么初始化结束之后,就开始更新操作。

复制代码
void update(int num,int le,int ri,int x,int y)
{
    if(x<=le&&y>=ri)
    {
        tre[num]++;
        laz[num]++;
        return ;
    }
    pushdown(num);
    int mid=(le+ri)/2;
    if(x<=mid)
        update(num*2,le,mid,x,y);
    if(y>mid)
        update(num*2+1,mid+1,ri,x,y);
}
复制代码
这一段是对区间[x,y]上的每个气球都涂色一次,当然你也可以涂z次色,无非就是额外再传一个变量代表涂色次数嘛。然后依然是分区间查找,一直到目标区间[x,y]包含了当前区间[le,ri],那么就对tre[num]和laz[num]操作,代表对这个区间我这么修改,而这个区间的分区间也应该被做修改,但是这时候我为了节约时间,暂时不做修改,但是我把这个修改动作存在laz[num]里,下次需要的时候再修改。

       而下次是什么时候呢?就是当前区间比我需要的目标区间大的时候,我必须用到下面的值了,那么就迫不得已了,不能再懒惰下去了,必须往下修改了,这时候,我们就把之前堆积起来的懒惰标记pushdown了,于是就有了一个神奇的pushdown操作。

复制代码
void pushdown(int num)
{
    if(laz[num]!=0)
    {
        tre[num*2]+=laz[num];
        tre[num*2+1]+=laz[num];
        laz[num*2]+=laz[num];
        laz[num*2+1]+=laz[num];
        laz[num]=0;
    }
}
复制代码
 以上就是这个题的pushdown操作。当laz[num]!=0时,也就是这个点存在懒惰标记的时候,我们就要往下更新了,然而这个题是否判断laz[num]的有无对整体影响不大,但是有部分题再pushdown的同时会对整体有影响,例如取最小值的时候对于一段同时置为一个数,那么如果不判断0,就会把0给误pushdown下去,这就必须判断laz[num]的有无了。

        那么laz[num]本来是应该修改下去的,所以会对两个儿子节点的tre[num]有影响,这里因为是求加,所以采用的是+号,其它操作根据具体题目替换。同时,儿子节点的儿子节点也应该被更新,但是我们依然懒,赶一步走一步,所以此时依然不更新儿子节点的儿子节点,而是用更新儿子节点的laz标记来代替。

        回到之前的updata操作,在每次修改了儿子节点的tre[num]之后,正常情况下应该对父亲节点的tre[num]进行修改,但是此题父亲节点的tre[num]不会对后面的结果造成影响,所以这里就偷懒省略了这一步操作,实际操作中绝大部分题目都是不可以省略的,必须在更新完儿子节点的值之后再反过来更新父亲节点。

        最后依然是query操作。

复制代码
int query(int num,int le,int ri,int x)
{
    if(le==ri)
    {
        return tre[num];
    }
    pushdown(num);
    int mid=(le+ri)/2;
    if(x<=mid)
        return query(num*2,le,mid,x);
    else
        return query(num*2+1,mid+1,ri,x);
}
复制代码
与单点更新时的query非常相似(废话吼,线段树本来就是一个比较模板的东西),也是额外加了一个pushdown操作,原因与update的一样,最后也缺省了反过来对父亲节点的更新,原因同上,不再赘述。

        至此线段树的两种基本用法:单点更新和区间更新操作就已经介绍完毕了。相信大家如果能仔细看完的话,对于这个数据结构也应该有了一定的认识。而线段树还有扫描线、区间合并等高级用法,而且线段树作为一个数据结构,是必然会和其它算法发生化学反应的,例如矩阵、dp等操作都有可能被巧妙地嵌套在线段树上形成一个综合题,所以大家下去一定要多做题,多感悟,才能深入透彻地吃下这个知识点。

<---------------------------------------------------------------------------------------------------------------------------------------------------------->

复制代码
void update(int i,int k,int val,int q){//单个点的更新
    if( A[i].lt == k && A[i].rt == k ){
        if( q ){
            A[i].val += val;
        }else{
            A[i].val -= val;
        }
        return ;
    }
    if( k <= A[i<<1].rt ){
        update( i<<1,k,val,q );//自己体会
    }
    if( k>= A[i<<1|1].lt ){
        update( i<<1|1,k,val,q );
    }
    A[i].val = (A[i<<1].val + A[i<<1|1].val );
}

int query(int i,int lt,int rt){
    if( A[i].lt >= lt && A[i].rt <= rt ){
        return A[i].val;
    }
    int a = 0;
    int b = 0;
    if( lt <= A[i<<1].rt ){//左区间有涉及就向左查询
        a = query( i<<1,lt,rt );
    }
    if( rt >= A[i<<1|1].lt ){//右区间有涉及就向右查询
        b = query( i<<1|1,lt,rt );
    }
    return (a+b);
}
复制代码
 */

//-------线段树

/*
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define co(x) cout << (x) << endl
#define ci(x) cin >> (x)
#define sd(x) scanf("%d",&x)
#define sf(x) scanf("%lf",&x)
#define pc(x) printf("%c",x)
#define pd(x) printf("%d",x)
#define gcd(x,y) __gcd(x,y)
#define w(x) while(x)
#define fo(i,j,k) for(int (i) = (j); (i) < (k); (i)++)
#define en cout << endl;
#define INF 2147483645
#define Maxn 1000010

struct Node{
    int left,right;
    int val;
}A[Maxn<<2];

void Build(int i,int left,int right){
    A[i].left = left;
    A[i].right = right;
    if(left == right){//如果该结点是根节点就赋值
        ci(A[i].val);
        return ;
    }
    int mid = (left+right) >> 1;
    Build( i<<1,left,mid );//向两边递归
    Build(i<<1|1,mid+1,right );
    A[i].val = min( A[i<<1].val,A[i<<1|1].val );
    //取较小的值(其他类比)
}

void update(int i,int p,int val){
    if( A[i].left == A[i].right ){//找到根结点
        A[i].val = val;//修改
        return ;
    }
    if( p <= A[i<<1].right ){//如果结点P在比A[i]的右儿子的右区间小(在右儿子的区间内)
        update( i<<1,p,val );//向右儿子的左区间更新节点
    }
    if( p >= A[i<<1|1].left ){//如果比左儿子的左区间大,则向右更新节点
        update(i<<1|1,p,val);
    }//向两边同时递归更新节点的值,保证每个被影响的值都被更新
    A[i].val = min( A[i<<1].val,A[i<<1|1].val );
}
// 如果该行描述一次商品的重量的更改,则接下来为两个整数Pi,Wi,
// 表示位置编号为Pi的商品的重量变更为Wi

int query(int i,int left,int right){ //left为查询的区间
    if( A[i].left >= left && A[i].right <= right ){//在查询区间内,返回该节点的值??
        return A[i].val;
    }
    int a = INF,b = INF;
    if( left <= A[i<<1].right ){ //如果左范围在A[i]的右儿子的左边,就递归向左边查询
        a = query(i << 1,left,right);
    }
    if( right >= A[i<<1|1].left ){//如果右范围在A[i]的左儿子的右边,就递归向右查询
        b = query(i<<1|1,left,right);
    }
    return min(a,b);//回朔返回左右儿子的较小值
}

int main(){
    int N,M,a,b,c;
    while( sd(N)!=EOF ){
        Build(1,1,N);
        ci(M);
        fo(i,0,M){
            scanf("%d %d %d",&a,&b,&c);
            if(a){
                update(1,b,c);
            }else{
                printf("%d\n",query(1,b,c));
            }
        }
    }
}
 */

/*
 #include <bits/stdc++.h>
using namespace std;

const int MAXNODE = 2097152;
const int MAX = 1000003;
struct NODE{
    int value;        // 结点对应区间的权值
    int left,right;   // 区间 [left,right]
}node[MAXNODE];

int father[MAX];     // 每个点(当区间长度为0时,对应一个点)对应的结构体数组下标

//建树
void BuildTree(int i,int left,int right){ // 为区间[left,right]建立一个以i为祖先的线段树,i为数组下标,我称作结点序号
    node[i].left = left;    // 写入第i个结点中的 左区间
    node[i].right = right;  // 写入第i个结点中的 右区间
    node[i].value = 0;      // 每个区间初始化为 0
    if (left == right){ // 当区间长度为 0 时,结束递归
        father[left] = i; // 能知道某个点对应的序号,为了更新的时候从下往上一直到顶
        return;
    }
    // 该结点往 左孩子的方向 继续建立线段树,线段的划分是二分思想,如果写过二分查找的话这里很容易接受
    // 这里将 区间[left,right] 一分为二了
    BuildTree(i<<1, left, (int)floor( (right+left) / 2.0));
    // 该结点往 右孩子的方向 继续建立线段树
    BuildTree((i<<1) + 1, (int)floor( (right+left) / 2.0) + 1, right);
}

//更新节点
void UpdataTree(int ri){ // 从下往上更新(注:这个点本身已经在函数外更新过了)

    if (ri == 1)return; // 向上已经找到了祖先(整个线段树的祖先结点 对应的下标为1)
    int fi = ri / 2;        // ri 的父结点
    int a = node[fi<<1].value; // 该父结点的两个孩子结点(左)
    int b = node[(fi<<1)+1].value; // 右
    node[fi].value = (a > b)?(a):(b);    // 更新这个父结点(从两个孩子结点中挑个大的)
    UpdataTree(ri/2);       // 递归更新,由父结点往上找
}

//查询节点
int Max = -1<<20;
void Query(int i,int l,int r){ // i为区间的序号(对应的区间是最大范围的那个区间,也是第一个图最顶端的区间,一般初始是 1 啦)
    if (node[i].left == l && node[i].right == r){ // 找到了一个完全重合的区间
        Max = (Max < node[i].value)?node[i].value:(Max);
        return ;
    }
    i = i << 1; // get the left child of the tree node
    if (l <= node[i].right){ // 左区间有涉及
        if (r <= node[i].right) // 全包含于左区间,则查询区间形态不变
            Query(i, l, r);
        else // 半包含于左区间,则查询区间拆分,左端点不变,右端点变为左孩子的右区间端点
            Query(i, l, node[i].right);
    }
    i += 1; // right child of the tree
    if (r >= node[i].left){ // 右区间有涉及
        if (l >= node[i].left) // 全包含于右区间,则查询区间形态不变
            Query(i, l, r);
        else // 半包含于左区间,则查询区间拆分,与上同理
            Query(i, node[i].left, r);
    }
}
 */

//排泄组合

/*
int main(){
    int a[3];
    a[0]=1;a[1]=2;a[2]=3;
    do{
        cout<<a[0]<<" "<<a[1]<<" "<<a[2]<<endl;
    }while (next_permutation(a,a+3)); //参数3指的是要进行排列的长度
}//如果存在a之后的排列,就返回true。如果a是最后一个排列没有后继,返回false,每执行一次,a就变成它的后继
 */

/*---------kmp
int KMP(char *s,char *p){
    int ans = -1;
    nex[0] = 0;
    int lenp = strlen(p);//子串长度
    int lens = strlen(s);//母串长度
    for(int i = 1,k = 0; i < lenp; i++) {//先处理字串的nex数组
        while(k > 0 && p[i] != p[k]) {//如果说不匹配,就找到k前面字母对应的值,将该值赋给k,然后比较p[k]与p[i]
            k = nex[k-1];
        }
        if(p[i] == p[k]){
            k++;
        }
        nex[i] = k;
    }
    for(int i = 0,k = 0; i < lens; i++){//用nex数组进行比对
        while(k > 0 && s[i] != p[k]){
            k = nex[k-1];
        }
        if(s[i] == p[k]){
            k++;
        }
        if(k == lenp){
            ans = i - lenp + 1;
            break;
        }
    }
    return ans;
}
 */

//----------高精度

/*
#include <bits/stdc++.h>
using namespace std;
#define Maxn 1000
int A[Maxn];
int main(){
    int i,j,n;
    scanf("%d",&n);
    memset(A,0,sizeof(A));
    A[0] = 1;//第一位要设置为1,否则结果就全是0了
    for(int i = 2; i <= n; i++){
        int c = 0;
        for(j = 0; j < Maxn; j++){//模拟计算
            int s = A[j]*i + c;
            A[j] = s % 10;
            printf("%d\n",s%10);
            c = s/10;
        }
    }
    //是从后往前计算的,例如115,数组里存的就是511,然后去掉前导0,倒序输出即可
    for(j = Maxn -1; j >= 0; j--){//去掉前导0
        if( A[j] ){
            break;
        }
    }
    for(i = j; i >= 0; i--){
        printf("%d",A[i]);
    }
    cout << endl;
    return 0;
}
 */

/*背包问题
 *
 #include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;
#define Maxn 14000
int Val[Maxn];

int bag(int val[],int weight[],int M,int N)
{
    memset(Val,0,sizeof(Val));
    int maxn = 0;
    for(int i = 1; i <= N; i++)
    {
        for(int j = M; j >= weight[i]; j--)
        {
            if (Val[j] < (Val[j - weight[i]] + val[i]))
            {
                Val[j] = max(Val[j],Val[j - weight[i]] + val[i]);
            }
        }
    }
    return Val[M];
}

int main()
{
    int N,M;
    int val[Maxn];
    int weight[Maxn];
    scanf("%d%d",&N,&M);
    for(int i = 1; i <= N; i++)
    {
        scanf("%d",weight+i);
        scanf("%d",val+i);
    }
    printf("%d\n",bag(val,weight,M,N));
    return 0;
}
 */

/*
又是一道水题 hdu背包
Problem Description
电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。

Input
多组数据。对于每组数据:
第一行为正整数n,表示菜的数量。n<=1000。
第二行包括n个正整数,表示每种菜的价格。价格不超过50。
第三行包括一个正整数m,表示卡上的余额。m<=1000。

n=0表示数据结束。

Output
对于每组输入,输出一行,包含一个整数,表示卡上可能的最小余额。

Sample Input
1
50
5
10
1 2 3 2 1 1 2 3 2 1
50
0

Sample Output
-45
32

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string>
#include <string.h>
using namespace std;
#define Maxn 1005
int V[Maxn][Maxn];

int fuck_the_package(int n,int num[],int money){
    memset(V,0,sizeof(V));
    int maxn = 0;
    for(int i = 1; i < n; i++){
        for(int j = 0; j <= money; j++){
            if (j < num[i]){
                V[i][j] = V[i-1][j];
            }else{
                V[i][j] = max(V[i-1][j],V[i-1][j-num[i]]+num[i]);
            }
            if (V[i][j] > maxn){
                maxn = V[i][j];
            }
        }
    }
    return maxn;
}

int main(){
    int n,money;
    while(scanf("%d",&n) != EOF && n != 0){
        int num[n+1];
        memset(num,0,sizeof(num));
        for(int i = 1; i <= n; i++){
            scanf("%d",num+i);
        }
        sort(num+1,num+n+1);
        scanf("%d",&money);
        // printf("%d\n",fuck_the_package(n,num,money-5));
        // printf("%d\n",num[n]);
        if (money < 5){
            printf("%d\n",money);
        }else{
            printf("%d\n",money - fuck_the_package(n,num,money-5) - num[n]);
        }
    }
}
 */
/*
上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走。但是她设计迷宫的思路不一样,首先她认为所有的通道都应该是双向连通的,就是说如果有一个通道连通了房间A和B,那么既可以通过它从房间A走到房间B,也可以通过它从房间B走到房间A,为了提高难度,小希希望任意两个房间有且仅有一条路径可以相通(除非走了回头路)。小希现在把她的设计图给你,让你帮忙判断她的设计图是否符合她的设计思路。比如下面的例子,前两个是符合条件的,但是最后一个却有两种方法从5到达8。

Input
输入包含多组数据,每组数据是一个以0 0结尾的整数对列表,表示了一条通道连接的两个房间的编号。房间的编号至少为1,且不超过100000。每两组数据之间有一个空行。
整个文件以两个-1结尾。

Output
对于输入的每一组数据,输出仅包括一行。如果该迷宫符合小希的思路,那么输出"Yes",否则输出"No"。

Sample Input
6 8
5 3
5 2
6 4
5 6
0 0
8 1
7 3
6 2
8 9
7 5
7 4
7 8
7 6
0 0
3 8
6 8
6 4
5 3
5 6
5 2
0 0
-1 -1

Sample Output
Yes
Yes
No

#include <stdio.h>
#include <iostream>
#include <set>

using namespace std;
#define Maxn 100000+5
int father[Maxn];

void init() {
    for (int i = 0; i <= Maxn; i++) {
        father[i] = i;
    }
}

int find(int x) {
    if (x != father[x]) {
        father[x] = find(father[x]);
    }
    return father[x];
}

void mix(int a, int b) {
    int fx = find(a), fy = find(b);
    //printf("|||fx = %d  x = %d  fy = %d  y = %d  |||  ",fx,a,fy,b);
    if (fx > fy) {
        father[fx] = fy;
    } else {
        father[fy] = fx;
    }
}

int main() {
    int a, b;
    set<int> B;
    set<int>::iterator it;
    while (1) {
        init();
        bool flag = false;
        while (1) {
            cin >> a >> b;
            if (a == 0 && b == 0) {
                break;
            }
            if (a == -1 && b == -1) {
                return 0;
            }
            if (find(a) == find(b)) {
                flag = true;
                // break;
            }
            if (a == b) {
                continue;
            }
            mix(a, b);
            B.insert(a);
            B.insert(b);
        }
        int sum = 0;
        for (it = B.begin(); it != B.end(); it++) {
            if (father[*it] == *it) {
                sum++;
            }
        }
        if (flag) {
            printf("No\n");
        } else {
            if (sum > 1) {
                printf("No\n");
            } else {
                printf("Yes\n");
            }
        }
        B.clear();
    }
}
*/
/*
 *
字典书

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>

using namespace std;
#define Maxn 30

struct Node {
    long long int num;
    Node *next[Maxn];

    Node() {
        num = 0;
        for (int i = 0; i < Maxn; i++) {
            next[i] = NULL;
        }
    }
} jiangmu;

void insert(char A[]) {
    Node *p = &jiangmu;
    for (int i = 0; i < strlen(A); i++) {
        int xx = A[i] - ‘a‘;
        if (p->next[xx] == NULL) {
            p->next[xx] = new Node;
        }
        p = p->next[xx];
        p->num++;
    }
}

long long int search(char A[]) {
    Node *p = &jiangmu;
    for (int i = 0; i < strlen(A); i++) {
        int xx = A[i] - ‘a‘;
        if (p->next[xx] == NULL) {
            return 0;
        } else {
            p = p->next[xx];
        }
    }
    return p->num;
}

int main() {
    char A[30];
    while (gets(A) && A[0]) {
        insert(A);
    }
    while (gets(A)) {
        printf("%lld\n", search(A));
    }
}
/*
时间: 2024-10-07 05:16:25

luanqibazao的相关文章

回顾一下类和对象以及继承关系

# coding:utf-8 import traceback class A(object): __static_field = 'A_private static field' public_static_field = 'A_public_static_field' start_urls = "A_class_start_urls" _one_line = "A_one_line" def __init__(self): (filename, line_num

js知识体系的梳理一

今天简单的总结了js的一些东西,梳理下整个体系,每一次的总结都会有不同的收获:js总结一一.[获取元素]: 1.通过ID: var oBtn=document.getElementById('btn1'); var oDiv=document.getElementById('div1'); 2.通过标签:var aDiv=document.getElementsByTagName('div'); 操作一组元素就要用循环: for(初始值;循环条件;自增自减条件){} 有长度的用for循环 初始值

2~16进制间任意进制转换器

一.程序运行截图(仅列举部分) 正数: (1)10->8 (连续五次不输入程序会自动结束哦) (2)10->2 (3)16->10 (4)8->16  负数: (1)10->2 (2)10->2 (3)8->16  二.扩展功能 1.可以识别正负数并输出,当输出为二进制时会自动将其补为有符号位的八位或十六位. 2.能在2至16所有进制间任意转换: 3输入错误时会报错并让你重新输入: 4.加了一个没卵用的随机数来使最后一句话随机输出. 三.介绍函数及其使用的全局变量