dp复习——状压专题

今天复习了状压(预习)

特殊方格棋盘

题目描述

在n*n(n≤20)的方格棋盘上放置 n 个车,某些格子不能放,求使它们不能互相攻击的方案总数。

#include <bits/stdc++.h>
using namespace std;
const int maxn=(1<<20)-1;
typedef long long ll;
int n,m,x,y,s,a[25];
ll f[maxn];
int lowbit(int x) {return x&-x;}
int main() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) {
        scanf("%d%d",&x,&y);//(x,y)处不可放车
        a[x]+=1<<(y-1);//a[x]数组标记第i行状态,第j列不可放车
    }
    f[0]=1;//初始化转移条件
    int Maxn=(1<<n)-1;//每行有2的n次方可能,转为二进制2的n次方-1记录
    for(int S=1;S<=Maxn;S++) {
        int cnt=0;
        for(int i=S;i;i-=lowbit(i)) cnt++;//S状态下已放入多少车(又为前cnt行,题目要求可得每行只能放一个且每行都必须放即每行有且只能放一个for(int i=S;i;i-=lowbit(i)) {
            if(!(a[cnt]&lowbit(i))) {//与前cnt行状态不矛盾
                s=S^lowbit(i);//上一行状态
                f[S]+=f[s];//上一行状态转移
            }
        }
    }
    printf("%lld",f[Maxn]);//末尾状态所得方案数
    return 0;
}

 互不侵犯

题目描述

在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。 

#include <bits/stdc++.h>
using namespace std;
const int maxn=1<<10;
int n,k,cnt[maxn];
typedef long long ll;
ll ans,dp[20][maxn][100];
bool vis[maxn];
int main() {
    scanf("%d%d",&n,&k);
    int Maxn=(1<<n)-1;
    for(int i=0;i<=Maxn;i++) {
        int temp=i;
        while(temp) {
            if(temp%2==1) cnt[i]++;
            temp/=2;
        }
    }
    for(int i=0;i<=Maxn;i++) {
        if(((i<<1)&i)==0) vis[i]=1;
    }
    for(int i=0;i<=Maxn;i++) {
        if(vis[i]&&cnt[i]<=k) dp[1][i][cnt[i]]=1;
    }
    for(int i=2;i<=n;i++) {
        for(int j=0;j<=Maxn;j++) {
            if(vis[j]) {
                for(int s=0;s<=Maxn;s++) {
                    if(!vis[s]) continue;
                    if((s&j)!=0||((s<<1)&j)!=0||((s>>1)&j)!=0) continue;
                    for(int l=k;l>=cnt[j];l--) {
                        dp[i][j][l]+=dp[i-1][s][l-cnt[j]];
                    }
                }
            }
        }
    }
    for(int i=0;i<=Maxn;i++) ans+=dp[n][i][k];
    printf("%lld
",ans);
    return 0;
}

旅游景点 Tourist Attractions

题目描述

FGD想从成都去上海旅游。在旅途中他希望经过一些城市并在那里欣赏风景,品尝风味小吃或者做其他的有趣的事情。经过这些城市的顺序不是完全随意的,比如说FGD不希望在刚吃过一顿大餐之后立刻去下一个城市登山,而是希望去另外什么地方喝下午茶。

幸运的是,FGD的旅程不是既定的,他可以在某些旅行方案之间进行选择。由于FGD非常讨厌乘车的颠簸,他希望在满足他的要求的情况下,旅行的距离尽量短,这样他就有足够的精力来欣赏风景或者是泡MM了^_^.

整个城市交通网络包含N个城市以及城市与城市之间的双向道路M条。城市自1N依次编号,道路亦然。没有从某个城市直接到它自己的道路,两个城市之间最多只有一条道路直接相连,但可以有多条连接两个城市的路径。任意两条道路如果相遇,则相遇点也必然是这N个城市之一,在中途,由于修建了立交桥和下穿隧道,道路是不会相交的。

每条道路都有一个固定长度。在中途,FGD想要经过K(K<=N-2)个城市。成都编号为1,上海编号为N,而FGD想要经过的N个城市编号依次为2,3,…,K+1. 举例来说,假设交通网络如下图。

FGD想要经过城市2,3,4,5,并且在2停留的时候在3之前,而在4,5停留的时候在3之后。那么最短的旅行方案是1-2-4-3-4-5-8,总长度为19。注意FGD为了从城市2到城市4可以路过城市3,但不在城市3停留。这样就不违反FGD的要求了。并且由于FGD想要走最短的路径,因此这个方案正是FGD需要的。

输入格式

第一行包含 3 个整数 N(2<=N<=20000),M(1<=M<=200000),K(0<=K<=20),意义如上所述。以下 M 行,每行包含 3 个整数 X,Y,Z,(1<=X<y<=n, 1<="Z=1000)表示城市 X与 Y 之间有一条双向道路。你可以认为输入文件使得一定能自成都到上海以及任何 FGD 想要去的城市。"下一行包含一个整数 G(0<=G<=K*(K-1)/2)。以下 G 行,每行包含 2 个整数 X Y,(2<=X,Y<=K+1)表示 FGD 想要在游览城市Y 之前,一定要游览城市 X。你可以认为至少存在一种满足所有限制的游览方案。

样例省略啦

这个要和最短路知识结合了♦

#include <bits/stdc++.h>
using namespace std;
const int maxn=1<<20,maxv=2e4+10,Inf=0x3f3f3f3f;
#define pa pair<int,int>
typedef long long ll;
int n,m,k,t,x,y,cnt,ans,d[maxv],head[maxv],a[25],dis[25][25],f[maxn][25];
bool vis[maxv];
struct Edge{
    int to,next,dis;
}e[maxn];
void add(int u,int v,int z) {
    e[++cnt].next=head[u];
    head[u]=cnt;
    e[cnt].to=v;
    e[cnt].dis=z;
}
void Dijkstra(int x) {
    priority_queue<pa,vector<pa>,greater<pa> >q;//队列优化算法
    for(int i=1;i<=n;i++) {
        d[i]=Inf;
        vis[i]=0;
    }
    d[x]=0;
    q.push(make_pair(0,x));
    while(!q.empty()) {
        int now=q.top().second;
        q.pop();
        if(vis[now]) continue;
        vis[now]=1;
        for(int i=head[now];i;i=e[i].next) {
            if(d[now]+e[i].dis<d[e[i].to]) {
                d[e[i].to]=d[now]+e[i].dis;
                q.push(make_pair(d[e[i].to],e[i].to));
            }
        }
    }
    for(int i=1;i<=k+1;i++) dis[x][i]=d[i];
    dis[x][0]=d[n];//记录i到n的最短路
}
int main() {
    scanf("%d%d%d",&n,&m,&k);
    int Maxn=(1<<k)-1;//k为限制,分别记录k个点每个点是否到达
    int u,v,z;
    for(int i=1;i<=m;i++) {
        scanf("%d%d%d",&u,&v,&z);
        add(u,v,z);
        add(v,u,z);//建图,无向
    }
    for(int i=1;i<=k+1;i++) Dijkstra(i);//计算第i个点到各个点最短距离
    scanf("%d",&t);
    for(int i=1;i<=t;i++) {
        scanf("%d%d",&x,&y);
        a[y]+=1<<(x-2);//位置左移,优化空间
     //到达第y个点前,必须先游览过第x个点;
      但是与最短路径经过y点不矛盾,可以路过不游览y点
} memset(f,
-1,sizeof(f));//距离可能为0,故将其初始化-1 f[0][1]=0;//起始转移条件 for(int s=0;s<=Maxn;s++) {//枚举游览状态 for(int i=1;i<=k+1;i++) {//前状态游览城市位置 if(f[s][i]!=-1) { for(int j=2;j<=k+1;j++) {//后状态游览城市位置 if((s&a[j])==a[j]) {//检查s状态下经过第i个点前状态是否满足限制 int to=(s|(1<<(j-2)));//或运算取后位置状态 if(f[to][j]>f[s][i]+dis[i][j]||f[to][j]==-1) f[to][j]=f[s][i]+dis[i][j];//s状态(前状态)第i个城市(前位置)转移为to状态(后状态)第j个城市(后位置)取最优;或to状态j位置还未被赋值将其赋值 } } } } } ans=Inf; for(int i=1;i<=k+1;i++) { if(f[Maxn][i]!=-1) ans=min(ans,f[Maxn][i]+dis[i][0]);//因为可以经过城市而不游览,所以状态满足Maxn时(即2~k+1个城市均已游览过) 末位置不一定是k+1但一定是2~k+1中的一个数;
     枚举Maxn状态下不同末位置求其最小值 } printf(
"%d",ans); return 0; }//lsyl

在这里插入图片描述

原文地址:https://www.cnblogs.com/1999-09-21Karry-erfs/p/13189510.html