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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

源哥每日一题第十三弹 百练4124:海贼王之伟大航路 状压dp

發布時間:2024/8/26 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 源哥每日一题第十三弹 百练4124:海贼王之伟大航路 状压dp 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

連接:http://bailian.openjudge.cn/practice/4124

題意:從1到n走過所有點恰好一次最短時間。亂搞的話會完美的超時(階乘級別的復雜度,雖然范圍很小,但是也足夠超時了)。

思路:先想一個不太成熟的思路。用dp[j][s]表示。s記錄的是每個點是否被走過的狀態。而dp[s][j]表示的是從1走到j狀態所用的最小時間。這樣的思路成不成立呢?首先,考慮初始值。開始是在1號點,那么dp[1][1]自然就是0了,其他就是max;另外,題面說只要遍歷每一個點,而于順序的話,并沒有要求,也就是說,通過任何一種演變方式到達該狀態的方式都是等價的,滿足了動態規劃無后效性的要求。同時,對于每一個時刻,當前位置存儲的值都是當前最優解,既問題具有最優子結構性質。同時,對于每一個演變,我們可以在dp[s][j]的基礎上,推出當前狀態的值可以通過上一步演變就到達的狀態進行更新,這也就是所謂“人人為我”的過程。dp方程也好想,既:

dp[i][k] = min(dp[i][k],dp[i_pre][k_pre]+G[i_pre][i]);

接下來就是比較重要的問題了:如何表示這些狀態,以及進行狀態之間的計算呢?

用16個bool?太麻煩了!換一種思路:因為只有16個數,將他們編成二進制編碼101010101010100……每一位代表當前位置所代表的點是否被走過,這樣的話,只需要2^16個無符號shortint(實際用int)就可以表示所有可能的狀態啦。

第一個問題是解決了,可如何進行狀態間的變換呢?請把c語言程序設計翻到xxx頁,有關位運算的章節:

&, 這個東西叫按位與,既每一位依次比較一樣就是1,不一樣就是0。平時常用的判斷奇偶性的n&1就是最簡單的應用。

|,這東西叫按位或,鍵位有點怪,一般在enter附近,意思是每一位依次比較有1就是1,全0就是0

^,按位異或,也是中文輸入法下省略號的打法。官方的話是相同為0,不同為1,我的理解就是不帶進位的加法。

~,取反,在tab上面,int下的話就是~x = -x-1 最常用的那個while(~scanf)用的就是這個原理(~0 = -0-1 = -1 = EOF)。

<< >> 左移右移 不多說,乘2除2

?

一些比較清奇的用法

從低位到高位,取n的第m位

return (n >> (m-1)) & 1;
從低位到高位.將n的第m位置1
return n | (1 << (m-1));
從低位到高位,將n的第m位置0

return n & ~(1 << (m-1));

(x&-x)只保留最低位的1

具體用法的話讀代碼,體會一下:

#include <iostream> #include <cstdio> #include <cstring> #include <algorithm>using namespace std;void read(){ #ifndef ONLINE_JUDGEfreopen("D:\\fengyu\\Jiang_C\\.vscode\\in.txt","r",stdin);freopen("D:\\fengyu\\Jiang_C\\.vscode\\out.txt","w",stdout); #endif } int G[20][20]; int dp[20][(1<<16)+5]; int main() { read();int n;while (cin >> n) {memset(dp,0x3f,sizeof(dp));for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {cin >> G[i][j];}}dp[1][1] = 0;int ans = (1<<n)-1;for (int k = 1; k <= ans; k++) {for (int i = 1,_i = 1; i <= n; i++,_i<<=1) {if (k&_i)for (int j = 1, _j = 1; j <= n; j++,_j<<=1) {if (i!=j && k&_j)dp[i][k] = min(dp[i][k],dp[j][k^_i]+G[j][i]);}}}cout << dp[n][ans] << endl;}return 0; }

?

轉載于:https://www.cnblogs.com/fengyuzhicheng/p/9153254.html

總結

以上是生活随笔為你收集整理的源哥每日一题第十三弹 百练4124:海贼王之伟大航路 状压dp的全部內容,希望文章能夠幫你解決所遇到的問題。

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