2018/7/6-纪中某C组题【jzoj1192,jzoj1397,jzoj1736】
前言
全體爆零,十分開心
正題
T1:矩陣
大意
就是N個矩陣,然后進行矩陣乘法(n?mn?m和m?pm?p的矩陣相乘就會變成n?pn?p的矩陣并且運算次數是n?m?pn?m?p),然后求最小乘法運算次數。
考試時
一直以為會是圖論,然后想來想去(什么網絡流,SPFA啊),然后就是不會做。
解題思路
首先講解一下矩陣乘法的性質:
假設有兩個矩陣A和B
A?B≠B?AA?B≠B?A
由此因為題目說矩陣一點可以進行相乘,所以前一個矩陣的寬一定等于后一個矩陣的長,然后就可以進行區間dp:
f[i][j]f[i][j]表示將i~ji~j合并為一個矩陣的最少運算次數
然后枚舉中間分割線midmid表示i~midi~mid,mid+1~jmid+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]表示只剩下i~ji~j的區間時指針在左/右邊距離k。
然后我們現在重新定義這條鏈為1~n1~n,nn為這條鏈的長度,然后我們要一個一個的去掉,所以我們是枚舉一個w(n?1~1)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][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=1~n)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】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 文档忘记保存怎么办文档忘记保存怎么办恢复
- 下一篇: 2018/7/7-纪中某C组题【jzoj