希望某大佬不要打我,我真的是按键盘的顺序起的标题啊。
出于某种不知名的原因我不敢咕这篇博客……
反思:
考试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
不会。