19-10-21-CSP-S模拟81-O

希望某大佬不要打我,我真的是按键盘的顺序起的标题啊。

%%%某大佬

出于某种不知名的原因我不敢咕这篇博客……

反思:

考试T1想了一个错的$dp$,然后理所嘤当的错了。

T1T2T3丢了三个暴力,然后T1因为读反了于是废到10分。

打暴力一定要稳稳稳!!!

结果:

45
Miemeng 10
03:09:33
10
03:09:33
0
03:09:34
20
03:09:34

垫底啦垫底啦……

Sol:

T1

状压$dp$

$70\%$蒜法大家都会吧。

(如果嫌公式看不清可以右键开一下放大)

设$dp_{i,s,t}$为放置了前$i$行,其中$s$为第$i$行的选择状态,$t$为第$i-1$行的选择状态,我们只要保证第$i$行是合法的就可以向下转移。

时间复杂度:$\Theta(N(2^M)^3)$

$100\%$算法

真是调(diao)死我了。

因为我们发现枚举第$i-1$行有点费,于是试图使用$i$的状态来转移。

那么我们就一定要存一下覆盖状态以便排除错误情况。

于是变化,设$dp_{i,s,t}$为放置好前$i$行,其中$s$为第$i$行的覆盖状态,$t$为第$i$行的选择状态。

那么一定有$cover_t \subseteq s$($cover_t$指选择为$t$时的覆盖情况)

于是枚举子集并去除不合法的情况。

时间复杂度就优化到$\Theta(N2^M3^M)$就可以A了。

#include <iostream>
#include <cstring>
#include <cstdio>

//#include "debug.h"

#define N 20
#define S (1<<11)+110

using namespace std;

int dp[N][S][S];
int val[N][S];
int pos[N];
int cst[N][N];
int lines,cols;
void getmin(int &a,int b){ a=min(a,b); }
int main(){
	char st[20];
	scanf("%d%d",&lines,&cols);
	for(int i=1;i<=lines;i++){
		scanf("%s",st+1);
		for(int j=cols;j>=1;j--){
			pos[i]=(pos[i]<<1)+st[j]-'0';
		}
	}
	for(int i=1;i<=lines;i++)
		for(int j=1;j<=cols;j++)
			scanf("%d",&cst[i][j]);
	for(int i=1;i<=lines;i++){
		for(int s=0;s<1<<cols;s++){
			for(int j=1;j<=cols;j++){
				int k=1<<(j-1);
				if(s&k) val[i][s]+=cst[i][j];
			}
		}
	}
	int maxs=(1<<cols)-1;
	memset(dp,0x3f,sizeof dp);
	for(int s=0;s<1<<cols;s++)
		getmin(dp[1][((s<<1)&maxs)|s|(s>>1)|pos[1]][s],val[1][s]);
	for(int i=1;i<lines;i++){
		for(int w=0;w<1<<cols;w++){
			int wf=((w<<1)&maxs)|w>>1|w;
			for(int s=0;s<1<<cols;s++){
				if((s|w)!=maxs)continue;
				for(int t=s;t>0;t=(t-1)&s){
					getmin(dp[i+1][wf|pos[i+1]|t][w],dp[i][s][t]+val[i+1][w]);
				}
				getmin(dp[i+1][wf|pos[i+1]][w],dp[i][s][0]+val[i+1][w]);
			}
		}
	}
	int ans=0x7fffffff;
	for(int t=0;t<1<<cols;t++)
		getmin(ans,dp[lines][maxs][t]);
	printf("%d\n",ans);
}

T2

不会。

T3

不会。

猜你喜欢

转载自www.cnblogs.com/kalginamiemeng/p/Exam20191021.html