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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

2018/7/6-纪中某C组题【jzoj1192,jzoj1397,jzoj1736】

發布時間:2023/12/3 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 2018/7/6-纪中某C组题【jzoj1192,jzoj1397,jzoj1736】 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

全體爆零,十分開心


正題


T1:矩陣

大意

就是N個矩陣,然后進行矩陣乘法(n?mn?mm?pm?p的矩陣相乘就會變成n?pn?p的矩陣并且運算次數是n?m?pn?m?p),然后求最小乘法運算次數。

考試時

一直以為會是圖論,然后想來想去(什么網絡流,SPFA啊),然后就是不會做。

解題思路

首先講解一下矩陣乘法的性質:
假設有兩個矩陣A和B
A?BB?AA?B≠B?A
由此因為題目說矩陣一點可以進行相乘,所以前一個矩陣的寬一定等于后一個矩陣的長,然后就可以進行區間dp:
f[i][j]f[i][j]表示將iji~j合并為一個矩陣的最少運算次數
然后枚舉中間分割線midmid表示imidi~midmid+1jmid+1~j分開合并,然后所有的取最小值。

代碼

#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int n,a[501],b[501],f[501][501]; int main() { memset(f,127/3,sizeof(f)); scanf("%d",&n); for (int i=1;i<=n;i++) { scanf("%d%d",&a[i],&b[i]); f[i][i]=0;//初始化 } for (int j=2;j<=n;j++) { for (int i=j-1;i>=1;i--) { for (int mid=i;mid<j;mid++) f[i][j]=min(f[i][j],f[i][mid]+f[mid+1][j]+a[i]*b[mid]*b[j]); //動態轉移 } } printf("%d",f[1][n]);//輸出 }

T2:圓盤取數

大意

有一個圓盤,上面有n個數,指針開始時指向第一個數,之后可以取走離指針距離不超過k里面的數,取走數代價是數的值,然后指針移動一格需要消耗代價為當前剩下最大的數。求取走所有數的最小代價。

考試時

開始時題目理解錯了,然后后來改正后打了個貪心,然后WA

解題思路

首先我們可以發現取數字無論何時取走都需要固定的代價,代價就是所有數的和,所以我們只需要求指針轉動的代價就好了。

首先我們先在不移動指針的情況下取走數字

然后將剩余部分展開

然后這就是一條鏈了,然后我們可以順路收掉所有的所以每次剩下的一點只會是一段區間。
因為現在指針離最左邊和最右邊剛好都是k,移動一格也只能多取一個數,所有我們就可以把k的問題去掉了。
之后我們就可以進行區間dp
f[i][j][0/1]f[i][j][0/1]表示只剩下iji~j的區間時指針在左/右邊距離k。
然后我們現在重新定義這條鏈為1n1~nnn為這條鏈的長度,然后我們要一個一個的去掉,所以我們是枚舉一個w(n?11)w(n?1~1)表示目前的長度然后枚舉i表示開頭。
之后我們要求f[i][j][0/1]f[i][j][0/1]了,我們有三種方式轉移到f[i][j]f[i][j],我們拿在左邊來計算,一是原本指針就在那個方向的左邊,那么這樣移動一格就好了

f[i?1][j][0]+max[i?1][j]f[i?1][j][0]+max[i?1][j]
然后另一種是左一格的情況轉一圈過來取
f[i?1][j][1]+(n?w)?max[i?1][j]f[i?1][j][1]+(n?w)?max[i?1][j]
最后一種是右一格的情況轉一圈過來取
f[i][j+1][1]+(n?w+1)?max[i][j+1]f[i][j+1][1]+(n?w+1)?max[i][j+1]
動態轉移方程
f[i][j][0]=minf[i?1][j][0]+max[i?1][j],f[i?1][j][1]+(n?k)?max[i?1][j],f[i][j+1][1]+(n?k+1)?max[i][j+1]f[i][j][0]=minf[i?1][j][0]+max[i?1][j],f[i?1][j][1]+(n?k)?max[i?1][j],f[i][j+1][1]+(n?k+1)?max[i][j+1]

然后求右邊的反過來就好了


代碼

#include<cstdio> #include<algorithm> #include<cstring> #define mins(x,y,z) min(x,min(y,z)) using namespace std; int n,k,t,answ,ans,a[2010],f[2010][2010][2],m[2010][2010]; int main() {freopen("data10.in","r",stdin);scanf("%d%d",&n,&k);n=n-2*k-1;for (int i=-k;i<=n+k;i++){scanf("%d",&t);answ+=t;//計算取數代價if (i>=1&&i<=n) a[i]=t;//去掉開始可以取的}for (int i=1;i<=n;i++){int maxs=0;for (int j=i;j<=n;j++)m[i][j]=(maxs=max(maxs,a[j]));//預處理區間最大}ans=2147483647/3;for (k=n-1;k>=1;k--){f[0][k][0]=f[0][k][1]=700000000;f[n-k+1][n+1][0]=f[n-k+1][n+1][1]=700000000;//防止取到界外for (int i=1;i<=n-k+1;i++){int j=i+k-1;f[i][j][0]=mins(f[i-1][j][0]+m[i-1][j],f[i-1][j][1]+(n-k)*m[i-1][j],f[i][j+1][1]+(n-k+1)*m[i][j+1]);//動態轉移f[i][j][1]=mins(f[i][j+1][1]+m[i][j+1],f[i][j+1][0]+(n-k)*m[i][j+1],f[i-1][j][0]+(n-k+1)*m[i-1][j]);//動態轉移}}for (int i=1;i<=n;i++)ans=min(ans,min(f[i][i][0],f[i][i][1])+a[i]+answ);//求最小代價printf("%d",ans); }

T3:撲克游戲

大意

一顆無窮大的完全二叉樹

你有n張數值不同的牌,如果你將一張牌放到了一個節點那么代價就是num?(dep?1)num?(dep?1)并且封鎖子樹,然后要求所有的牌放在樹上的最小代價。

考試時

敲了一個貪心每次盡量平均分成兩半子樹傳,然后只剩一個數時返回信息,最后進行統計

貪心代碼

#include<cstdio> #include<algorithm> using namespace std; int tot,n,a[10001],s,v[10001]; int tx(int x,int num,int sum,int dep) {if (x<=1&&dep!=0) return sum*dep;int s=0,k=0;++tot;for (int i=1;i<=n;i++)if (v[i]==num){if (s*(dep+1)+a[i]*(dep+1)<=sum*(dep+1)/2) s+=a[i],v[i]=tot,k++;else v[i]=tot+1;}tot++;int w=tot;return tx(k,w-1,s,dep+1)+tx(x-k,w,sum-s,dep+1); } int main() { scanf("%d",&n);for (int i=1;i<=n;i++)scanf("%d",&a[i]),s+=a[i];sort(a+1,a+1+n);printf("%d",tx(n,0,s,0)); }

然后全WA

解題思路

就是合并果子(復制粘貼)
原理:
合并果子時最后一堆需要消耗sum+=a[i](i=1n)sum+=a[i](i=1~n),然后假設上一堆的需要果子子集S,那么代價就是sum+=x(x?S)sum+=x(x?S)。那么我們發現該數合并的次數其實就是這道題放在的深度加1。

正確原理是哈夫曼樹(自己看我也不會)。

代碼

#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int k,x,num,n1,n2,a1[30001],a2[30001],t[20001],w,sum; int main() {scanf("%d",&num);memset(a1,127/3,sizeof(a1));memset(a2,127/3,sizeof(a2));for (int i=1;i<=num;i++){scanf("%d",&x);t[x]++;//桶}for (int i=1;i<=20000;i++){while (t[i])//通排序{t[i]--;a1[++n1]=i;}}int i=1,j=1;k=1;while (k<num){if (a1[i]<a2[j])//取最小值{w=a1[i];i++;}else{w=a2[j];j++;}if (a1[i]<a2[j])//取第二次{w+=a1[i];i++;}else{w+=a2[j];j++;}a2[++n2]=w;//加入第二個隊列k++;//計算合并次數sum+=w;//計算價值}printf("%d",sum); }

合并果子O(n)O(n)原理詳見:
https://blog.csdn.net/mr_wuyongcong/article/details/80030964


后續

矩陣乘法白學了,合并果子白學了,區間dp白學了。這就是第一天的收獲233。

總結

以上是生活随笔為你收集整理的2018/7/6-纪中某C组题【jzoj1192,jzoj1397,jzoj1736】的全部內容,希望文章能夠幫你解決所遇到的問題。

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