日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【CF1209E】Rotate Columns【状压DP】【位运算】【贪心】

發布時間:2023/12/3 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【CF1209E】Rotate Columns【状压DP】【位运算】【贪心】 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題意:給一個N×MN \times MN×M的矩陣,可以進行任意多次操作將一列輪換,求每一行的最大值之和的最大值。多組數據。

Easy VersionN≤4N \leq 4N4,M≤100M \leq100M100

Hard VersionN≤12N \leq 12N12,M≤2000M \leq2000M2000

看這數據范圍顯然是個狀壓

相當于是每一行只能選一個

f(i,S)f(i,S)f(i,S)表示當前到iii,已經選了SSS的最大值

記憶化搜索一波,暴力轉一下

復雜度O(4nn2m)O(4^nn^2m)O(4nn2m) 可以通過Easy Version

#include <iostream>#include <cstdio>#include <cstring>#include <cctype>using namespace std;int n,m;int a[20][105],dp[105][1<<4];int Move(int s){return (s>>1)|((s&1)<<(n-1));}int dfs(int pos,int s){if (dp[pos][s]) return dp[pos][s];if (s==(1<<n)-1) return 0;if (pos==m) return 0;int &ans=dp[pos][s];for (int t=0;t<(1<<n);t++){if (s&t) continue;int res=dfs(pos+1,s|t);int mx=0;for (int k=0,cur=t;k<n;k++){int sum=0;cur=Move(cur);for (int i=0;i<n;i++)if ((1<<i)&cur)sum+=a[i][pos];mx=max(mx,sum);}ans=max(ans,res+mx);}return ans;}void solve(){memset(dp,0,sizeof(dp));scanf("%d%d",&n,&m);for (int i=0;i<n;i++)for (int j=0;j<m;j++)scanf("%d",&a[i][j]);printf("%d\n",dfs(0,0));}int main(){int T;scanf("%d",&T);while (T--) solve();return 0;}

復雜度里有MMM,過不了HV

考慮如何消掉M

我們發現因為只有NNN行,所以最多只有NNN列選了數

能否快速確定這NNN列?

貪心!

我們把列按最大值從大到小排序,如果前面的一列沒選數而后面的選了

那我們完全可以改成前面沒有選的,一定更優

因為只能選NNN個數,所以只用考慮最大的NNN

復雜度O(4nn3)O(4^nn^3)O(4nn3)

#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #include <algorithm> using namespace std; int n,m; int a[20][2005],dp[2005][1<<12],ord[2005]; int Move(int s){return (s>>1)|((s&1)<<(n-1));} int dfs(int pos,int s) {if (dp[pos][s]) return dp[pos][s];if (s==(1<<n)-1) return 0;if (pos==m) return 0;int &ans=dp[pos][s];for (int t=0;t<(1<<n);t++){if (s&t) continue;int res=dfs(pos+1,s|t);int mx=0; // printf("%d",t);for (int k=0,cur=t;k<n;k++){int sum=0;cur=Move(cur); // printf("->%d",cur);for (int i=0;i<n;i++)if ((1<<i)&cur)sum+=a[i][ord[pos]];mx=max(mx,sum);} // puts("");ans=max(ans,res+mx);}return ans; } int mx[2005]; inline bool cmp(const int& a,const int& b){return mx[a]>mx[b];} void solve() {memset(dp,0,sizeof(dp));memset(mx,0,sizeof(mx));scanf("%d%d",&n,&m);for (int i=0;i<n;i++)for (int j=0;j<m;j++)scanf("%d",&a[i][j]),mx[j]=max(mx[j],a[i][j]);for (int i=0;i<m;i++) ord[i]=i;sort(ord,ord+m,cmp);m=min(n,m);printf("%d\n",dfs(0,0)); } int main() {int T;scanf("%d",&T);while (T--) solve();return 0; }

然而發現慢得飛起

我們發現:在dp的時候,會花費O(n)O(n)O(n)找出最佳的旋轉方案,而這個O(n)O(n)O(n)是在O(4n)O(4^n)O(4n)的基礎上的

為什么不預處理呢

然后得到了O(2nn3+4nn)O(2^nn^3+4^nn)O(2nn3+4nn)

#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #include <algorithm> using namespace std; int n,m; int a[20][2005],mem[2005][1<<12],dp[2005][1<<12],ord[2005]; int dfs(int pos,int s) {if (dp[pos][s]) return dp[pos][s];if (s==(1<<n)-1) return 0;if (pos==m) return 0;int &ans=dp[pos][s];for (int t=0;t<(1<<n);t++)if (!(s&t))ans=max(ans,dfs(pos+1,s|t)+mem[ord[pos]][t]);return ans; } int mx[2005]; inline bool cmp(const int& a,const int& b){return mx[a]>mx[b];} void solve() {memset(dp,0,sizeof(dp));memset(mx,0,sizeof(mx));memset(mem,0,sizeof(mem));scanf("%d%d",&n,&m);for (int i=0;i<n;i++)for (int j=0;j<m;j++)scanf("%d",&a[i][j]),mx[j]=max(mx[j],a[i][j]);for (int i=0;i<m;i++) ord[i]=i;sort(ord,ord+m,cmp);m=min(n,m);for (int pos=0;pos<m;pos++)for (int s=0;s<(1<<n);s++)for (int k=0;k<n;k++){int sum=0;for (int i=0;i<n;i++)if (s&(1<<i))sum+=a[(i+k)%n][ord[pos]];mem[ord[pos]][s]=max(mem[ord[pos]][s],sum); } printf("%d\n",dfs(0,0)); } int main() {int T;scanf("%d",&T);while (T--) solve();return 0; }

然而還是慢得飛起

這玩意還有多組數據

我們發現這一句

if (!(s&t))

是求和sss沒有交集的ttt

能否快速得到這玩意呢?

還真不行

但我們可以換一種實現方式

直接遞推實現dp

這樣從上一維轉移過來只用枚舉子集

可以用這句

for (int t=s;t;t=((t-1)&s))

這樣就只枚舉了sss的子集

復雜度?

加上枚舉sss

一共是

∑i=0nCni2i\sum_{i=0}^{n}C_n^i2^ii=0n?Cni?2i

=∑i=0nCnn?i2i=\sum_{i=0}^{n}C_n^{n-i}2^i=i=0n?Cnn?i?2i

=(1+2)n=3n=(1+2)^n=3^n=(1+2)n=3n

所以復雜度是O(2nn3+3nn)O(2^nn^3+3^nn)O(2nn3+3nn)

#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #include <algorithm> using namespace std; int n,m; int a[12][2000],mx[2000],pos[2000],mem[2000][1<<12],dp[2000][1<<12]; inline bool cmp(const int& a,const int& b){return mx[a]>mx[b];} void solve() {scanf("%d%d",&n,&m);memset(mx,0,sizeof(mx));for (int i=0;i<n;i++)for (int j=0;j<m;j++)scanf("%d",&a[i][j]),mx[j]=max(mx[j],a[i][j]);for (int i=0;i<m;i++) pos[i]=i;sort(pos,pos+m,cmp);m=min(n,m);for (int p=0;p<m;p++)for (int s=0;s<(1<<n);s++){mem[p][s]=0;for (int k=0;k<n;k++){int sum=0;for (int i=0;i<n;i++)if ((1<<i)&s)sum+=a[(i+k)%n][pos[p]];mem[p][s]=max(mem[p][s],sum);}} memset(dp,0,sizeof(dp));for (int s=0;s<(1<<n);s++) dp[0][s]=mem[0][s];for (int p=1;p<m;p++)for (int s=0;s<(1<<n);s++){for (int t=s;t;t=((t-1)&s))dp[p][s]=max(dp[p][s],dp[p-1][s^t]+mem[p][t]);dp[p][s]=max(dp[p][s],dp[p-1][s]); }printf("%d\n",dp[m-1][(1<<n)-1]); } int main() {int T;scanf("%d",&T);while (T--) solve();return 0; }

過于毒瘤

總結

以上是生活随笔為你收集整理的【CF1209E】Rotate Columns【状压DP】【位运算】【贪心】的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。