棋盘DP

主要是在棋盘上的DP,棋盘上每个点的转移状态基本上都是已知的

//https://www.luogu.com.cn/problem/P1896
//压状dp
//由于时间复杂度达到了指数级复杂度,所以搜索不能进行
//考虑动态规划,dp i,j,k表示 第i行,使用了j个国王,此时的状态是k
// 对于第i行,若要求此时符合条件,有: 第i行中的棋子,两两互不相邻,即check(a|b)=true
// 对于第i-1行,有与第i行的国王不相邻,所以使用&运算即可 
#include<bits/stdc++.h>
using namespace std;
const int N=12,M=1<<10,K=110;
int n,m,cnt[M],dp[N][M][K];
vector<int>state,head[M];//把状态存入state中 
int count(int x)//计算1的个数 
{
    int res=0;
    for(int i=0;i<n;i++)
        if((x>>i&1)==1) res++;
    return res;
}
bool check(int x) //检查相邻两位是否连续为1 
{
    for(int i=0;i<n;i++)
        if((x>>i&1)&&(x>>i+1&1)) return false;
    return true;
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<1<<n;i++)
        if(check(i)) 
            state.push_back(i),cnt[i]=count(i);
    for(int i=0;i<state.size();i++)
        for(int j=0;j<state.size();j++){
            int a=state[i],b=state[j];
            if(!(a&b)&&check(a|b)) head[i].push_back(j); 
        }    
    dp[0][0][0]=1;
    for(int i=1;i<=n+1;i++)
        for(int j=0;j<=m;j++)
            for(int a=0;a<state.size();a++)
                for(int b:head[a]){
                    int c=cnt[state[a]];
                    if(j>=c) dp[i][j][a]+=dp[i-1][j-c][b];
                }
    cout<<dp[n+1][m][0];
    return 0;
}

 

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

//首先提供二维dp,二维dp的思路为ij表示i行j列时的可以取得最大值
//类似于贪心,先进行第一遍循环,取到最优,然后把第一遍取的数全变为0,再进行第二遍的取
//但是这种方法并不一定是全局的最优解

//0    0    2    3    0    0    0
//0    0    3    0    0    0    0
//0    0    3    0    0    0    0
//0    0    0    0    0    0    0
//0    0    0    0    4    0    0
//0    0    0    0    4    0    0
//0    0    0    0    4    0    0
//如图,走第一遍可得出终点时最大值为20,去掉已经走过的点后图如下:                        
//0    0    0    3    0    0    0
//0    0    0    0    0    0    0
//0    0    0    0    0    0    0
//0    0    0    0    0    0    0
//0    0    0    0    0    0    0
//0    0    0    0    0    0    0
//0    0    2    0    0    0    0 
//然后会发现我们无法全部走完,也正符合贪心策略,“只注重眼前的利益”,因此此题使用二维dp绝非正解,上代码:
#include<bits/stdc++.h>
using namespace std;
const int N=10;
int dx[]={0,1},dy[]={1,0},n,mp[N][N];
int dp[N][N],res,ans;
int main()
{
    cin>>n;
    int a,b,c;
    while(cin>>a>>b>>c&&a+b+c>0) mp[a][b]=c;
//    for(int i=1;i<=n;i++){
//        for(int j=1;j<=n;j++) cout<<mp[i][j]<<' ';
//        cout<<endl;
//    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            dp[i][j]=max(dp[i-1][j]+mp[i][j],dp[i][j-1]+mp[i][j]);
    res=dp[n][n],ans=dp[n][n];
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(dp[i][j]==res&&dp[i][j]!=0) res-=mp[i][j],mp[i][j]=0,i=0,j=0;
        }
    }
    memset(dp,0,sizeof dp);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            dp[i][j]=max(dp[i-1][j]+mp[i][j],dp[i][j-1]+mp[i][j]);
    cout<<ans+dp[n][n];
    return 0;
}

//正确思路:  
//四维dp,用ijkl表示思维,ij表示第一遍走的,kl表示第二遍走的,然后进行n^4的dp
//如果遇到相同的点就直接减去 
#include<bits/stdc++.h>
using namespace std;
const int N=10;
int n,m,dp[N][N][N][N],mp[N][N];
int main()
{
    cin>>n;
    int a,b,c;
    while(cin>>a>>b>>c&&a+b+c>0) mp[a][b]=c;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                for(int l=1;l<=n;l++){
                    dp[i][j][k][l]=max(max(dp[i-1][j][k-1][l],max(dp[i-1][j][k][l-1],dp[i][j-1][k][l-1])),dp[i][j-1][k-1][l])+mp[i][j]+mp[k][l];
                    if(i==k&&j==l) dp[i][j][k][l]-=mp[k][l];
                }
    cout<<dp[n][n][n][n];
    return 0;
}

 

posted @ 2023-11-14 10:07  o-Sakurajimamai-o  阅读(31)  评论(0)    收藏  举报
-- --