双端队列优化搜索,最优性剪枝,可行性剪枝,优化搜索顺序,排除等效冗余

杨辉三角:

 

 

//https://www.luogu.com.cn/problem/P1118


//最优性剪枝:

//由高中知识可得,abcd四个数符合杨辉三角的数相乘,即
//res=a+3*b+3*c+d,前面的常数项也就是杨辉三角的数字
//根据此结论,进行剪枝

//由于暴力枚举全排列+部分剪枝不可过,所以要考虑方法性剪枝
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int cow[N],n,m,k;
int res[N];
bool vis[N];
//当前搜索到第cnt个数,当前的数是num,前cnt个数的和是sum
bool dfs(int cnt,int num,int sum)
{
    if(sum>k) return false;
    if(cnt==n){
        if(sum==k){
            res[cnt]=num;
            return true;
        }
        return false;
    }
    vis[num]=true;//当前的数字标记,被搜过了
    for(int i=1;i<=n;i++){
        if(!vis[i]&&dfs(cnt+1,i,sum+cow[cnt]*i)){ //如果没被搜过并且dfs为true
            res[cnt]=num;
            return true;
        }
    }
    vis[num]=false;//退出标记,为先前的循环做准备
    return false;
}
int main()
{
    cin>>n>>k;
    cow[0]=cow[n-1]=1;
    if(n>1)
        for(int i=1;2*i<n;i++)
            cow[i]=cow[n-i-1]=(n-i)*cow[i-1]/i;
    //for(int i=0;i<=n;i++) cout<<cow[i]<<' ';
    if(dfs(0,0,0))
        for(int i=1;i<=n;i++) cout<<res[i]<<' ';
        
    return 0;
}

 

 1 //https://codeforces.com/contest/1381/problem/B
 2 
 3 
 4 /*
 5 观察序列
 6 对任意的两个相邻数p[i],p[i+1];
 7 如果p[i]>p[i+1] 
 8 1,两者是从不同的数组过来的? 答案是不可能
 9 因为p[i]在p[i+1]前面
10 2.两者可以是从相同的数组过来的
11 如果p[i]<p[i+1],自行考虑,两种都可以
12 那么将两个推广成三个
13 发现如果p[i]>p[i+2]或者p[i]>p[i+1]或者p[i+1]>p[i+2],那么他们一定在一个数组里
14 所以可推更多....
15 
16 例如: 6
17 4 3 2 5 1 11 9 12 8 6 10 7
18 那么 12 8 6 10 7 肯定在一个数组里,因为12把他们都堵住了,怎么?还有比12大的?
19 同理, 11把9堵住, 11 9
20 所以可以分为: 4 3 2/ 5 1/ 11 9/ 12 8 6 10 7
21 并且只能这样分,这些数组可以任意组合,不影响答案,如果可以构成长度为n的,那么就yes
22 */
23 #include <bits/stdc++.h>
24 #define int long long
25 using namespace std;
26 const int N=1e6+10,mod=1e9+7;
27 bitset<4020>dp;
28 int n,t,a[N],f[N],res,num,ans,m,k;
29 bool vis[N];
30 void solve()
31 {
32     cin>>n;
33     for(int i=0;i<=n;i++) dp[i]=0;
34     int len=0,x=0; dp[0]=1;
35     for(int i=0;i<2*n;i++){
36         cin>>k;
37         if(k>x) dp|=dp<<len,len=1,x=k;
38         else len++;
39     }
40     dp|=dp<<len;
41     return cout<<(dp[n]?"Yes":"No")<<endl,void();
42 }
43 signed main()
44 {
45     std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
46     cin>>t;
47     while(t--){
48         solve();
49     }
50     return 0;
51 }
 1 // https://www.luogu.com.cn/problem/P1433
 2 
 3 // 将所有的奶酪看做成状态,0为未被吃,1为被吃
 4 // 枚举所有的状态,f[i][j]表示,在吃到第i个奶酪下,没有吃到j奶酪的距离
 5 // 用i对所有的j全都更新一边最短距离即可
 6 #include<bits/stdc++.h>
 7 using namespace std;
 8 const int N=5e4+10;
 9 int n,m;
10 double f[25][N],dis[500][500];
11 double x[N],y[N],res=2e9;
12 double distance_(double x1,double x2,double y1,double y2)
13 {
14     return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
15 }
16 int main()
17 {
18     cin>>n;
19     for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
20     x[0]=y[0]=0;
21     for(int i=0;i<=n;i++)
22         for(int j=i+1;j<=n;j++) dis[j][i]=dis[i][j]=distance_(x[i],x[j],y[i],y[j]);
23     memset(f,0x7f,sizeof f);
24     for(int i=1;i<=n;i++) f[i][1<<(i-1)]=dis[0][i];
25     for(int k=1;k<(1<<n);k++){
26         for(int i=1;i<=n;i++){
27             if(!(k&1<<(i-1))) continue;
28             for(int j=1;j<=n;j++){
29                 if(!(k&1<<(j-1))||i==j) continue;
30                 f[i][k]=min(f[i][k],f[j][k-(1<<(i-1))]+dis[j][i]);
31             }
32         }
33     }
34     for(int i=1;i<=n;i++) res=min(res,f[i][(1<<n)-1]);
35     printf("%.2lf",res); 
36     return 0;
37 }

 

// https://www.luogu.com.cn/problem/P4667

//94 dijkstra
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
bool vis[N];
int n,m,dist[N];
typedef pair<int,int>pii;
int e[N],ne[N],h[N],idx,w[N];
void add(int a,int b,int c)
{
    w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool dijkstra()
{
    priority_queue<pii,vector<pii>,greater<pii> >que;
    que.push({0,1}),dist[1]=0;
    while(!que.empty()){
        pii now=que.top(); que.pop();
        int x=now.second,y=now.first;
        if(vis[x]) continue; vis[x]=true;
        for(int i=h[x];~i;i=ne[i]){
            int j=e[i];
            if(dist[j]>y+w[i]){
                dist[j]=y+w[i];
                que.push({dist[j],j});
            }
        }
    }
    return (dist[(n+1)*(m+1)]<0x3f3f3f3f/2);
}
int main() 
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=(n+1)*(m+1);i++) dist[i]=0x3f3f3f3f,h[i]=-1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            char x; cin>>x;
            if(x=='/'){
                int u=(m+1)*(i-1)+j+1,v=(m+1)*i+j;
                int u_=u-1,v_=v+1;
                add(u,v,0),add(v,u,0),add(u_,v_,1),add(v_,u_,1);
            }
            else{
                int u=(m+1)*(i-1)+j,v=(m+1)*i+j+1;
                int u_=u+1,v_=v-1;
                add(u,v,0),add(v,u,0),add(u_,v_,1),add(v_,u_,1);
            }
        }
    if(dijkstra()) cout<<dist[(n+1)*(m+1)];
    else cout<<"NO SOLUTION";
    return 0;
}


//双端队列优化
/*
很明显是一道搜索的题。

我们可以把电路板上的每个格点(横线与竖线的交叉点)看作无向图中的结点。
若两个结点x和 y是某个小方格的两个对角,则在 x与 y之间连边。
若该方格中的标准件(对角线)与x到y的线段重合,则边权为 0;若垂直相交,则边权为 1 (说明需要旋转 1 次才能连通)。
然后,我们在这个无向图中求出从左上角到右下角的最短距离,就得到了结果。

这是一个边权要么是 0 ,要么是 1的无向图。在这样的图上,我们可以通过双端队列广度搜索计算。
算法的整体框架与一般的广度优先搜索类似,只是在每个结点上沿分支扩展时稍作改变。
如果这条分支是边权为0的边,就把沿改分支到达的新节点从队头入队;
如果这条分支是边权为1的边,就像一般的广度优先搜索一样从队尾入队。
这样一来,我们就仍然能保证任意时刻广度优先搜索队列中的结点对应的距离值都具有"两段性"和“单调性”
每个结点从队头出队时,就能得到从左上角到该结点的最短距离。

因为每个结点只需要访问一次,所以算法的时间复杂度为O(n×m)。
*/
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=505;
char map[maxn][maxn];
bool used[maxn][maxn];
int dx[4]={1,1,-1,-1},
    dy[4]={1,-1,1,-1};
int n,m,dis[maxn][maxn];
deque< pair<int,int> >q,empty;
bool turn(int x1,int y1,int x2,int y2)
{
    int x=map[(x1+x2)/2][(y1+y2)/2]=='/'?1:-1;
    return x==(y1-y2)/(x1-x2);
}
void bfs()
{
    memset(used,0,sizeof(used));
    memset(dis,0x3f3f3f3f,sizeof(dis));
    q=empty;q.push_back(make_pair(0,0));dis[0][0]=0;//清零!!!
    while(!q.empty())
    {
        int a=q.front().first,
            b=q.front().second;
        q.pop_front();used[a][b]=true;
        for(int i=0;i<4;i++)
        {
            int x=dx[i]+a,y=dy[i]+b;
            if(x<0||n<x||y<0||m<y||used[x][y])
                continue;
            if(turn(a,b,x,y))//如果电线不直接连接两个点,即代价为1
            {//就把结点按正常入队
                dis[x][y]=min(dis[x][y],dis[a][b]+1);//注意要取最小值
                q.push_back(make_pair(x,y));
            }
            else//如果电线直连,那么到(a,b),(x,y)两个点的步数是一样的
            {//换句话说,他们的层级是一样的,由于一般的广搜的队列层级是单调递增的,所以为了保证这个性质,将(x,y)从队首入队.
                dis[x][y]=min(dis[x][y],dis[a][b]);
                q.push_front(make_pair(x,y));
            }
            if(x==n&&y==m) return;
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
            scanf("%s",map[i]);
        if((n+m)%2)
        {
            puts("NO SOLUTION");
            continue;
        }
        bfs();
        printf("%d\n",dis[n][m]);
    }
    return 0;
}

 

posted @ 2023-10-16 19:02  o-Sakurajimamai-o  阅读(124)  评论(0)    收藏  举报
-- --