提高课二刷疑难杂题

方格取数

#include<bits/stdc++.h>
using namespace std;
const int N=11;
int g[N][N];
int f[2*N][N][N]; // 假设有两个小朋友在同时从A走向B,g[k][i1][i2]=w,表示第一个小朋友
//走到第(i1,k-i1),第二个小朋友走到(i2,k-i2)时的最小值为w
int main(){
    int n;
    cin >> n;
    int a,b,c;
    while(cin>>a>>b>>c,a){
        g[a][b]=c;
    }
    for(int k=2;k<=2*n;k++){//从A走到B必然要走2*n-2次(曼哈顿距离),k要从2开始因为一步没走时在(1,1)点
        for(int i1=1;i1<=n;i1++){
            for(int i2=1;i2<=n;i2++){
                int j1=k-i1,j2=k-i2;
                if(j1<=n&&j1>=1&&j2<=n&&j2>=1){
                    //我们惊奇的发现,两人可能同时到达同一格子,这时只取一次t就行了
                    //不可能出现1人走到了另一人已经走过的格子上这种情况
                    int &x=f[k][i1][i2];
                    int t = g[i1][j1];
                    if(i1!=i2) t += g[i2][j2];
                    x = max(x, f[k-1][i1-1][i2-1]+t);
                    x = max(x, f[k-1][i1-1][i2]+t);
                    x = max(x, f[k-1][i1][i2-1]+t);
                    x = max(x, f[k-1][i1][i2]+t);
                }
            }
        }
    }
    cout << f[2*n][n][n] << endl;
    return 0;
}

参考题解

友好城市

// not because they are easy,but because they are hard.
//本题更像一个思维题,先想暴力怎么做 然后去优化,发现规律如果北岸城市编号是递增的,那么
//在所有合法的答案中所有北岸城市对应的南岸城市的编号一定是递增的否则会出现交叉
#include<bits/stdc++.h>
#define x first
#define y second
const int N=5050;
using namespace std;
pair<int,int>h[N];
int f[N]; // f[i]=j 表示以第i个数为开始的最长上升子序列的长度为j
int n;
int main(){
    cin >> n;
    for(int i=1;i<=n;i++){
        cin >> h[i].x >> h[i].y;
        f[i]=1;
    }
    sort(h+1,h+n+1);
    int ans=0;
    for(int i=n;i>=1;i--){
        for(int j=i+1;j<=n;j++){
            if(h[i].y<h[j].y){
                f[i]=max(f[i],f[j]+1);
            }
            ans=max(ans,f[i]);
        }
    }
    cout << ans <<endl;
    return 0;
}

参考题解

最长公共上升子序列


//f[i][j]=k 表示 1~i  1~j 且以b[j]为结尾的最长上升公共子序列的长度为k
//状态转移:包含a[i] 则必须满足a[i]==b[j] 则f[i][j]=max(f[i-1][1~j-1]+1);
//不包含a[i]  f[i][j]=max(f[i][j],f[i-1][j]);
#include<bits/stdc++.h>
using namespace std;
const int N=3030;
int a[N],b[N];
int f[N][N];
int n;
int main(){
    cin >> n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int j=1;j<=n;j++) cin>>b[j];
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            f[i][j]=f[i-1][j];
            int maxv=1;
            if(a[i]==b[j]){
                
                for(int k=1;k<j;k++){
                    if(b[k]<b[j]){
                        maxv=max(maxv,f[i-1][k]+1);
                    }
                }
                  f[i][j]=max(f[i][j],maxv);
            }
          
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        ans=max(ans,f[n][i]);
    }
    cout << ans ;
    return 0;
}

参考题解

混合背包问题

//完全背包看成一类   , 多重背包 01背包看成一类
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int f[N];
int main(){
    cin >> n >> m;
    for(int i=1;i<=n;i++){
        int a,b,c;  // -1 01   0wanquan
        cin >> a >> b >> c;
        if(c==0){
            for(int j=a;j<=m;j++){
                f[j]=max(f[j],f[j-a]+b);
            }
        }
        else {
            if(c==-1) c=1;
            for(int k=1;k<=c;k=k*2){
                for(int j=m;j>=a*k;j--){
                    f[j]=max(f[j],f[j-a*k]+b*k);
                }
                c-=k;
            }
            if(c){
                for(int j=m;j>=c*a;j--){
                    f[j]=max(f[j],f[j-a*c]+b*c);
                }
            }
        }
    }
    cout << f[m];
    return 0;
}

潜水员问题

// 背包问题求最值问题:求体积不超过v的最大值 f[i][j]=0;
//                     求体积恰好为v的最大值:f[0][0]=0,f[其他][其他]=-0x3f3f3f3f
//                                  的最小值:f[0][0]=0,f[其他][其他]=0x3f3f3f3f
//                     求体积至少为v的最小值(只会求最小 潜水员) f[0][0]=0,f[其他][其他]=0x3f3f3f3f
//背包问题求方案数的问题:求体积不超过v的方案数:f[0][i]=1;
//                       求体积恰好为v的方案数:f[0][0]=1;
//                       求体积至少为v的方案数:f[0][0]=1;
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int f[N][N]; // 体积至少为i和j的最小值为k
int v1,v2,n;
int a[N],b[N],c[N];
int main(){
    cin >> v1 >> v2 >> n;
    for(int i=1;i<=n;i++){
        cin >> a[i] >> b[i] >> c[i];
    }
    memset(f,0x3f,sizeof f);
    f[0][0]=0;
    for(int i=1;i<=n;i++){
        for(int j=v1;j>=0;j--){
            for(int k=v2;k>=0;k--){
                f[j][k]=min(f[j][k],f[max(0,j-a[i])][max(k-b[i],0)]+c[i]);
            }
        }
    }
    cout << f[v1][v2];
    return 0;
}

机械分配

//注意背包问题是怎么输出方案的
#include<bits/stdc++.h>
using namespace std;
const int N=20;
int g[N][N];
int f[N][N];
int n,m;
int main(){
    cin >> n >> m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin >> g[i][j];
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){
            for(int k=0;k<=j;k++){
                f[i][j]=max(f[i][j],f[i-1][j-k]+g[i][k]);
            }
        }
    }
    cout<<f[n][m]<<endl;
    for(int i=n;i>=1;i--){
        for(int j=0;j<=m;j++){
            if(f[i][m]==f[i-1][m-j]+g[i][j]){
                cout<< i <<" " << j <<endl;
                m-=j;
                break;
            }
        }
    }
    return 0;
}

有依赖的背包问题

//thread:f[N][N]=k  表示以i为根节点体积不超过j的最大价值为k
//状态转移:不选择第i个..
// 选择第i个 f[i][j]=max(f[i][j],f[i][j-k]+f[son][k]) 0<=j<=m-v[i],0<=k<=j-v[u]
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1010;
int n,m;
int idx,h[N],e[N],ne[N],w[N],v[N];
//优化掉一维前:f[i][k][j] (k表示以i为根节点i一共有k个分叉即k个子树) 以i为根节点从前k个子树中选体积不超过j的最大值
int f[N][N];
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u){
    for(int i=v[u];i<=m;i++) f[u][i]=w[u];
    for(int i=h[u];i!=-1;i=ne[i]){
        int son=e[i];
        dfs(son);
        for(int j=m;j>=v[u];j--){//为什么要倒着枚举捏?因为要优化掉一维 优化掉哪一维捏?
            for(int k=0;j-k>=v[u];k++){ // 优化掉了第二维跟分组背包一样,详情请看小虎成员题解
                f[u][j]=max(f[u][j],f[u][j-k]+f[son][k]);
            }
        }
    }
 
}
int main(){
    cin >> n >> m;
    memset(h,-1,sizeof h);
    int st;
    for(int i=1;i<=n;i++){
        int a,b,c;
        cin >> a >> b >> c;
        v[i]=a,w[i]=b;
        if(c==-1) {
            st=i;
            continue;
        }
        add(c,i);
    }
    dfs(st);
    cout << f[st][m];
    return 0;
}

背包问题求具体方案数

//与普通求方案数不一样,本题是求最大体积不超过v ,且总价值最大的方案数,平常是求
// 体积不超过v的方案数, 本体不能想当然先求一遍01背包然后看f[i][j]是否等于f[n][m]
//如果等于则加一个答案,这样是错误的,会有重复,因为比如
// 16 200
// 17 200
// 18 200
// 19 200
// 20 200
//这几种方案是等价的,按照想当然,这几种方案会累加
// 还有一点 本题定义数组g的含义时 是恰好。为什么不定义成不超过?
//  因为定义成恰好 比较容易统计方案数 ,定义成不超过还要考虑到容斥
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int f[N][N]; // f[i][j] //表示从前i个物品中选其中体积恰好为j的最大价值
int g[N][N];// g[i][j] //表示从前i个物品中选,其中体积恰好为j的方案数
int n,m;
int v[N],w[N];
int mod=1e9+7;
int main(){
    cin >> n >> m;
    for(int i=1;i<=n;i++){
        cin >> v[i] >> w[i];
    }
    memset(f,-0x3f,sizeof f);
    f[0][0]=0;
    g[0][0]=1;
    int maxx=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){
            f[i][j]=f[i-1][j];
            if(j>=v[i]){
                f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
            }
            if(f[i][j]==f[i-1][j]){
                g[i][j]=(g[i][j]+g[i-1][j])%mod;//
            }
            if(f[i][j]==f[i-1][j-v[i]]+w[i]&&j>=v[i]){
                g[i][j]=(g[i][j]+g[i-1][j-v[i]])%mod;
            }
        }
    }
    // for(int i=1;i<=n;i++){
    //     for(int j=0;j<=m;j++){
    //         if(f[i][j]==f[i-1][j]){
    //             g[i][j]=(g[i][j]+g[i-1][j])%mod;//
    //         }
    //         if(f[i][j]==f[i-1][j-v[i]]+w[i]&&j>=v[i]){
    //             g[i][j]=(g[i][j]+g[i-1][j-v[i]])%mod;
    //         }
    //     }
    // }
    int tmp=0;;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){
            if(f[i][j]>tmp) tmp=f[i][j];
        }
    }
    int res=0;
    for(int j=0;j<=m;j++){
        if(tmp==f[n][j]){
            res=(res+g[n][j])%mod;
        }
    }
    cout << res;
    return 0;
}

小国王

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=12,M=1<<11;
vector<int>state;
vector<int>st[M];
int n,m;
int cnt[M];
int f[N][110][M];
int num(int x){
    int sum=0;
    while(x){
        if(x&1){
            sum++;
        }
        x=x>>1;
    }
    return sum;
}
signed main(){
    int n,m;
    cin >> n >> m;
    for(int i=0;i< 1<<n;i++){  // 注意这里不可以随意取到等号,取到等号是不合法的,取到等号时,i的2进制长度为n+1位
        if(!(i&(i>>1))){
            state.push_back(i);
            cnt[i]=num(i);
        }
    }
    f[0][0][0]=1;
    for(int i=0;i<(int)state.size();i++){
        for(int j=0;j<(int)state.size();j++){
            int a=state[i],b=state[j];
            if(!(a&b) && !(a&(b>>1)) && !(a&(b<<1))){
                st[i].push_back(j);
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){
            for(int k=0;k<(int)state.size();k++){
                int a=cnt[state[k]];
                if(j<a) continue;
                for(int l=0;l<(int)st[k].size();l++){
                     f[i][j][k]+=f[i-1][j-a][  st[k][l] ];
                }
            }
        }
    }
    int res=0;
    for(int i=0;i<(int)state.size();i++){
      //  cout << f[n][m][state[i]] << endl;
        res+=f[n][m][i];//注意这里加的是f[n][m][i] 不是f[n][m][state[i]],因为我们
        //在dp的过程中 用的是编号代替的状态本身,所以累加的时候应该用编号
    }
    cout << res;
    return 0;
}
//比较容易理解的版本
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=12,M=1<<11;
vector<int>state;
vector<int>st[M];
int n,m;
int cnt[M];
int f[N][110][M];
int num(int x){
    int sum=0;
    while(x){
        if(x&1){
            sum++;
        }
        x=x>>1;
    }
    return sum;
}
signed main(){
    int n,m;
    cin >> n >> m;
    for(int i=0;i< 1<<n;i++){  // 注意这里不可以随意取到等号,取到等号是不合法的,取到等号时,i的2进制长度为n+1位
        if(!(i&(i>>1))){
            state.push_back(i);
            cnt[i]=num(i);
        }
    }
    f[0][0][0]=1;
    for(int i=0;i<(int)state.size();i++){
        for(int j=0;j<(int)state.size();j++){
            int a=state[i],b=state[j];
            if(!(a&b) && !(a&(b>>1)) && !(a&(b<<1))){
                st[a].push_back(b);//这里与上个代码的不同,上个代码存的是编号
                //这个存的是状态
                //上个代码 st[i][j]=k 表示第k个状态在所有可以转移到第i个状态中排第j个可以转移到第i个状态的
                //这个是st[i][j]=k 表示k状态排第j个可以转移到i状态
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){
            for(int k=0;k<(int)state.size();k++){
                int a=cnt[state[k]];//这里不同
                if(j<a) continue;
                for(int l=0;l<(int)st[state[k]].size();l++){//这里也不同
                     f[i][j][state[k]]+=f[i-1][j-a][  st[state[k]][l] ];//这里不同
                }
            }
        }
    }
    int res=0;
    for(int i=0;i<(int)state.size();i++){
      //  cout << f[n][m][state[i]] << endl;
        res+=f[n][m][state[i]];//这里不同 state[i]=k   i是编号 k才是状态
    }
    cout << res;
    return 0;
}

选元素

//f[i][j]=k 表示从前i个数中选 且包含第i个数 当前已经选了j个数的最大值
//那么 第j-1个数 也就是倒数第二个数的下标t应该满足   i-k<=t<i
//状态转移  f[i][j]=max(f[i][j],f[t][j-1]+a[i]) (i-k<=t<i) 
//答案就应该是 res=max(res,f[i][x])  1=<i<=n
#include<iostream>
#include<cstring>
#define int long long
using namespace std;
const int N=220;
int n,k,x;
int f[N][N];
int a[N];
signed main(){
    cin >> n >> k >> x;
    
    for(int i=1;i<=n;i++) cin>>a[i];
    memset(f,-0x3f,sizeof f);
    f[0][0]=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=x;j++){
            for(int t=i-k;t<i;t++){
                if(t>=0) f[i][j]=max(f[i][j],f[t][j-1]+a[i]);
            }
        }
    }
    int res=-1;
    for(int i=n-k+1;i<=n;i++){  // 因为i从n-k+1开始算才能满足选的x可以覆盖到整个区间
        res=max(res,f[i][x]);
    }
    cout << res;
    return 0;
}

能量项链

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N=210,INF=0x3f3f3f3f;

int n;
int a[N];
int g[N][N];

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        a[i+n]=a[i];
    }
    memset(g,-0x3f,sizeof g);
    for(int len=1;len<=n;len++)
        for(int i=1;i+len-1<=n*2;i++)
        {
            int l=i,r=i+len-1;
            if(len==1)  g[l][r]=0;
            else for(int k=l;k<r;k++)    g[l][r]=max(g[l][r],g[l][k]+g[k+1][r]+a[l]*a[k+1]*a[r+1]);
            //这里的过程同石子合并,这里不难想到若将l到k的珠子合并之后会变成一个首是l而尾k+1的珠子;
            //同理若将k+1到r的珠子合并之后会变成一个首是k+1而尾r+1的珠子;
            //g[l][l]+g[l+1][r]+a[l]*a[l+1]*a[r+1] 可以理解为先把l+1到r合并然后在和l合并
        }
    int maxv=-INF;
    for(int i=1;i<=n;i++)
        maxv=max(g[i][i+n-1],maxv);//区间长度为n
    cout<< maxv <<endl;
    return 0;
}



加分二叉树

#include<bits/stdc++.h>
using namespace std;
const int N=35;
int f[N][N];//f[i][j]=k表示i~j的最大值为k
int g[N][N]; //g[i][j]=k  表示i~j中间的根节点为k
int n;
int w[N];
void path(int l,int r){
    if(l>r){
        return;
    }
    cout << g[l][r]<<" ";
    path(l,g[l][r]-1);
    path(g[l][r]+1,r);
}
int main(){
    cin >> n;
    for(int i=1;i<=n;i++){
        cin >> w[i];
        g[i][i]=i;  // 初始化
        f[i][i]=w[i];
    }
    for(int len=2;len<=n;len++){
        for(int l=1;l+len-1<=n;l++){
            int r=l+len-1;
            for(int k=l;k<=r;k++){
                int left,right;
                if(k==l){ //说明左子树为空
                    left=1;
                }
                else {
                    left=f[l][k-1];
                }
                if(r==k){  //说明右子树为空
                    right=1;
                }
                else {
                    right=f[k+1][r];
                }
                if(left*right+w[k]>f[l][r]){
                    f[l][r]=left*right+w[k];
                    g[l][r]=k;
                }
        }}
    }
    cout << f[1][n]<<endl;
    path(1,n);
    return 0;
}

树的最长路径

#include<iostream>
#include<cstring>
using namespace std;
const int N=10010;
int h[N*2],w[N*2],ne[N*2],idx,e[N*2];
void add(int a,int b,int c){
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
int n;
int f[N];
int dfs(int u,int father){ // 每次返回最长值
    int max1=0,max2=0;
    for(int i=h[u];i!=-1;i=ne[i]){
        int j=e[i]; // i 是idx
        //if(j==u) continue;判断是否是父节点不是这样判断的,你这个u是当前节点往其他方向延申的
        //这个j就不可能是u,j是从u发出到达的一个点,u怎么可能发出一个到达它自己的东西呢?
        if(j==father) continue;
        int tmp=dfs(j,u)+w[i];
      //  cout << tmp << endl;
        if(tmp>=max1){
            max2=max1;
            max1=tmp;
        }
        else if(tmp>max2){
            max2=tmp;
        }
    }
    f[u]=max1+max2;
    return max1;
}
int main(){
    memset(h,-1,sizeof h);
    cin >> n;
    for(int i=1;i<=n-1;i++){
        int a,b,c;
        cin >> a >> b >> c;
        add(a,b,c);
        add(b,a,c);
    }
    int res=0;
    dfs(1,-1);
    for(int i=1;i<=n;i++){
        res=max(res,f[i]);
    }
    cout << res;
    return 0;
}

树的中心

按照自己的理解写的 确实是比较麻烦
#include<iostream>
#include<cstring>
using namespace std;
const int N=10010;
int h[N*2],w[N*2],ne[N*2],idx,e[N*2];
void add(int a,int b,int c){
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
int n;
int f[N];
int max1[N],max2[N],up[N];
int num[N]; //num[i]=j 就表示第i个点的最长路径的下一个点是j
int dfs(int u,int father){ // 每次返回最长值
    for(int i=h[u];i!=-1;i=ne[i]){
        int j=e[i]; // i 是idx
        if(j==father) continue;
        int tmp=dfs(j,u)+w[i];
      //  cout << tmp << endl;
        if(tmp>=max1[u]){
            max2[u]=max1[u];
            max1[u]=tmp;
            num[u]=j;
        }
        else if(tmp>max2[u]){
            max2[u]=tmp;
        }
    }
    //f[u]=max1+max2;
    return max1[u];
}
//先要更新父节点的真实长度
void dfs2(int u,int father){
    for(int i=h[u];i!=-1;i=ne[i]){
        int j=e[i];//ishi idx
        if(j==father) continue;
        if(num[u]==j){ // u的最长路径经过子节点j了
            //max1[j]=max(max1[j],max2[u]+w[i]);
            //看一下能否更新j节点的最长路径和次长路径
            if(max2[u]+w[i]>max1[j]){
                max2[j]=max1[j];
                max1[j]=max2[u]+w[i];
                num[j]=u;
            }
            else if(max2[u]+w[i]>max2[j]){
                max2[j]=max2[u]+w[i];
            }
        }
        else {
            //max1[j]=max(max1[j],max1[u]+w[i]);
            //如果说最长路径被不经过j的路径更新的话 那么最长路径
            //也就不再经过j的子节点了
            if(max1[u]+w[i]>max1[j]){
                max2[j]=max1[j];
                max1[j]=max1[u]+w[i];
                num[j]=u;
            }
            else if(max1[u]+w[i]>max2[j]){
                max2[j]=max1[u]+w[i];
            }
        }
        dfs2(j,u);
    }
}
int main(){
    memset(h,-1,sizeof h);
    cin >> n;
    for(int i=1;i<=n-1;i++){
        int a,b,c;
        cin >> a >> b >> c;
        add(a,b,c);
        add(b,a,c);
    }
    int res=0x3f3f3f3f;
    dfs(1,-1);
    dfs2(1,-1);
    for(int i=1;i<=n;i++){
        res=min(res,max1[i]);
    }
    cout << res;
    return 0;
}

y总代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n;
int h[N],w[N*2],e[N*2],ne[N*2],idx;
int dn1[N],dn2[N],up[N],son1[N],son2[N];
void add(int a,int b,int c){
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
void dfs_down(int u,int father){
    for(int i=h[u];~i;i=ne[i]){
        int j=e[i];
        if(j==father) continue;
        dfs_down(j,u);//这里的dp要求子节点先被算出来
        if(w[i]+dn1[j]>=dn1[u]){
            dn2[u]=dn1[u];
            dn1[u]=w[i]+dn1[j];
            son2[u]=son1[u];
            son1[u]=j;
        }
        else if(w[i]+dn1[j]>dn2[u]){
            dn2[u]=w[i]+dn1[j];
            son2[u]=j;
        }
    }
    return;
}
void dfs_up(int u,int father){
    for(int i=h[u];~i;i=ne[i]){
        int j=e[i];
        if(j==father) continue;
        if(j==son1[u]){  //如果说d1[u]经过他的子节点j
            up[j]=max(up[u]+w[i],dn2[u]+w[i]);
        }
        else {
            up[j]=max(up[u]+w[i],dn1[u]+w[i]);
        }
        dfs_up(j,u); // 这里的dp要求父节点先被算出来
    }
    return;
}
int main(){
    memset(h, -1, sizeof h); //不要忘了邻接表初始化!
    cin >> n;
    for(int i=0;i<n-1;i++){
        int a,b,c;
        cin >> a >> b >> c;
        add(a,b,c),add(b,a,c);
    }
    dfs_down(1,-1);
    dfs_up(1,-1);
    int ans=0x3f3f3f3f;
    for(int i=1;i<=n;i++){
        ans=min(ans,max(up[i],dn1[i]));
    }
    cout << ans;
    return 0;
}

数星星

//细节很多,本题利用了欧几里得距离来记录哈希值,就是对于一个图形无论怎么变化
//对称或者翻转它的总体欧几里得距离是不变的(欧几里得总距离可以通过图形中任意两个点求欧几里得距离累加得到),可以利用这一性质进性hash
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N=110;
typedef pair<int,int>PII;
PII q[N*N]; //为什么要开N*N 因为 n*m的矩阵 至多有这么多个点
int dx[8]={-1,0,1,-1,1,-1,0,1},dy[8]={1,1,1,0,0,-1,-1,-1};
char g[N][N];
int n,m,num;
void bfs(int a, int b)
{
    q[num ++ ] = {a, b};
    g[a][b] = '0';
    queue<PII>p;
    p.push({a,b});
    while(p.size()){
        auto t=p.front();
        p.pop();
        for(int i=0;i<8;i++){
            int X=t.x+dx[i],Y=t.y+dy[i];
            if(X>=0&&X<n&&Y>=0&&Y<m&&g[X][Y]=='1'){
                q[num++]={X,Y};
                g[X][Y]='0';
                p.push({X,Y});
            }
        }
    }
}
double get_dist(PII a,PII b){
    return sqrt((b.y-a.y)*(b.y-a.y)+(b.x-a.x)*(b.x-a.x));
}
double get_hash(){
    double res=0;
    for(int i=0;i<num;i++){
        for(int j=i+1;j<num;j++){
            res+=get_dist(q[i],q[j]);
        }
    }
    return res;
}
char get_id(double key){
    static double hash[30];
    static int id=0;
    for(int i=0;i<id;i++){
        if(fabs(key-hash[i])<1e-8){ // 求绝对值
            return i+'a';
        }
    }
    hash[id++]=key;
    return id-1+'a';
}
int main(){
    cin >> m >> n;
    for(int i=0;i<n;i++) cin>>g[i];
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(g[i][j]=='1'){
                num=0;
                bfs(i,j);
                char f=get_id(get_hash());
                for(int k=0;k<num;k++){
                    g[q[k].x][q[k].y] = f;
                }
            }
            
        }
    }
    for(int i=0;i<n;i++) cout << g[i] << endl;
    return 0;
}

牛的学术圈II

//思维题
//假设g[a][b]=1 表示Ca>Cb 则 Ea<Eb
//证明 Ea>Eb g[b][a]=1&&g[a][b]!=1   即1
//证明 Ea<Eb g[a][b]=1&&g[b][a]!=1   即0
//证明是 ?  g[a][b]==0&&g[b][a]==0; 即?
#include<iostream>
#include<cstring>
#include<unordered_map>
using namespace std;
const int N=220;
unordered_map<string,int>id;
int g[N][N];//g[i][j]=1 表示Ca>Cb 则 Ea<Eb
int n,m;
int main(){
    cin >> m >> n;
    for(int i=0;i<n;i++){
        string tmp;
        cin >> tmp;
        id[tmp]=i;
    }
    string name[N];
    while(m--){
        for(int i=0;i<n;i++) cin>>name[i];
        for(int i=0;i<n;i++){
            int j=i+1;
            while(j<n&&name[j]>name[j-1]) j++;
            while(j<n){
                int a=id[name[i]],b=id[name[j]];
                g[a][b]=1;
                j++;
            }
        }
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(i==j) cout << "B";
            else if(g[i][j]==0&&g[j][i]==0) cout <<"?";
            else if(g[j][i]=1&&g[i][j]!=1) cout<<1;
            else cout <<0;
        }
        cout << endl;
    }
    return 0;
}

本质上升序列

#include <iostream>

using namespace std;
const int N = 1000;

int f[N];
string ss;

int main()
{
	ss = "tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhfiadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqijgihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmadvrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl";

	for(int i = 0; i < ss.size(); i ++ )
	{
		f[i] = 1;
		for(int j = 0; j < i;j ++ )
		{
			if(ss[i] > ss[j]) f[i] += f[j];
			if(ss[i] == ss[j]) f[i] -= f[j];
		}
	}
	
	long long res = 0;
	for(int i = 0; i < ss.size(); i ++ ) res += f[i]; 
	cout << res << endl;
	return 0;
}

参考题解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值