dp基礎

傳球遊戲
題目描述:
上體育課的時候,小蠻的老師經常帶着同學們一起做遊戲。這次,老師帶着同學們一起做傳球遊戲。
遊戲規則是這樣的:n個同學站成一個圓圈,其中的一個同學手裏拿着一個球,當老師吹哨子時開始傳球,每個同學可以把球傳給自己左右的兩個同學中的一個(左右任意),當老師再次吹哨子時,傳球停止,此時,拿着球沒傳出去的那個同學就是敗者,要給大家表演一個節目。
聰明的小蠻提出一個有趣的問題:有多少種不同的傳球方法可以使得從小蠻手裏開始傳的球,傳了m次以後,又回到了小蠻手裏。兩種傳球方法被視作不同的方法,當且僅當這兩種方法中,接到球的同學按接球順序組成的序列是不同的。比如有3個同學1號、2號、3號,並且假設小蠻爲1號,球傳了3次回到小蠻手裏的方式有1->2->3->1和1->3->2->1,共2種。
輸入格式
輸入文件ball.in共一行,有兩個用空格隔開的整數n,m
40%的數據滿足:3<=n<=30,1<=m<=20
100%的數據滿足:3<=n<=30,1<=m<=30
輸出格式
輸出文件ball.out共一行,有一個整數,表示符合題意的方法數。
樣列輸入
3 3
樣列輸出
2

分析:
題目分析:

其實我覺得這跟上臺階是很相似的。也就是第 i 個 是由第 i-1 個 和 第 i+1個轉移而來。
很明顯就是一個動態規劃了吧。
我想想
於是我們就知道狀態轉移方程了

dp[i][j] 表示 在i 個人 j 次傳
然後 dp[i][j] = dp[i-1][j-1] + dp[i+1][j-1] 由於是個圈
不想加倍了……取個模 dp[i][j] = dp[(i-1+n)%n][j-1] + dp[(i+1)%n][j-1];
就可以了

int main(){
  readdata();//這個都懂吧0.0
  dp[0][0] = 1; //i個人  j次傳 
  for(int j=1 ;j<=m ;j++)
    for(int i=0 ;i<n ;i++)
    {
      dp[i][j] = dp[(i-1+n)%n][j-1] + dp[(i+1)%n][j-1]; 
    }  
  printf("%d",dp[0][m]);
  return 0;
 }

純潔的買賣
題目描述:
ALEJ並不是財迷,但是作爲純潔黨的偉大領袖,不掙錢,純潔的事業怎麼能堅持下去!純潔黨現在已經有M(1<=M<=100000)元經費了。ALEJ有一個富II代朋友,叫做HSW,HSW特別喜歡高價收藏一些餐巾紙(毛澤東用過的)、襪子(恐龍穿過的)、馬桶墊(還珠格格坐過的)、紅領巾(毛新宇戴過的)等等,總之,沒有他不買的。因此ALEJ想通過HSW這個大財主,去實現倒買倒賣擁有儘可能多的錢。
有N(1<=N<=100)件物品供他選擇,
ALEJ每件物品的買入價爲ci元,
HSW的收藏價爲ri元。
每向HSW賣出一件物品i之後,還要向政府上交c[i]元的稅。每種物品的數量都是無限的。
ALEJ想知道,通過一次買賣(種類、數量沒有限制)後,純潔黨的經費能有多少。
輸入格式
第一行兩個整數,N,M。之後N行,每行兩個整數:c[i],r[i]
輸出格式
一個整數,表示一次買賣後手裏最多的錢數。
樣例輸入
3 17
2 4
5 6
3 7
樣例輸出
22

分析:
很明顯啊,完全揹包問題。不多贅述。….
然後記住要交稅……所以w[i] = r[i] - 2*c[i] (這裏很關鍵,必須減2倍);
你只能求得利潤
如果你說只減一倍不加本金…..那就大錯特錯了你一定想得通。。當然也許有蒙對的可能….但機率爲0,因爲我試了一下……..
所以必須 那樣做 求得最大利潤 + M (本金).

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;


inline int read() //其實這題不優化也行
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int m,n;
int c[110],w[110],r;
int f[100005];

void readdata(){
  n = read();m = read(); 
  for(int i = 1 ;i<=n ;i++)
  {
    c[i] = read();
    r=read();
    w[i] = r-2*c[i];
  }
}
//完全揹包 
int main(){
 readdata();
  for(int i=1 ;i<=n ;i++) 
     for(int j=c[i];j<=m ;j++)
     {
      f[j] = max(f[j] , f[j-c[i]] + w[i]);
     }
 printf("%d",f[m]+m);
 return 0;
}

多邊形遊戲
題目描述
多邊形遊戲是一種在一個具有n個頂點的多邊形上進行的遊戲。每個頂點有個權值(整數)。如圖1是一個n=4對應多邊形,每個頂點上都有一個整數,每條邊都有一個運算符+或者*,所有邊按從1到n進行編號。
這裏寫圖片描述
遊戲都首先移除一條邊,接下來可以進行如下操作:
選擇一條邊E和與之相關聯的點V1和V2,用一個新的點替換它們,新點上的整數爲V1,V2上的整數用E上的操作符運算後的結果。
沒有邊時遊戲結束,遊戲得分就是最後剩下的那個頂點上的整數。
對於圖1中的多邊形,如果遊戲者首先去掉3,然後依次去掉1、4、2,最後得分將是0。
這裏寫圖片描述
請你寫一個程序,對於給定的多邊形,計算出可能得到的最高分,並列出第一步移除哪些邊可以得到這個最高分。
關於輸入
輸入第一行是一個正整數n(3<=n<=50),表示多邊形的邊數。
接下來一行是這個多邊形的描述,包含n條邊和n個頂點,加號邊用t表示,乘號邊用x表示,頂點用頂點上標的整數表示,輸入按照邊的編號從1到n的順序給出。
關於輸出
第一行輸出可能得到的最高分。
第二行輸出一些邊的列表,只有第一步移除的邊在這個列表中才可能得到最高分。每條邊都用這個邊上的編號表示,列表必須是升序的。
例子輸入
4
t -7 t 4 x 2 x 5
例子輸出
33
1 2

分析:
我想說這真的是一道要寫很多的動歸
原因在於 負負得正………
於是我們得有一個來存 max 一個存min (或者多加一維)
然後就是枚舉去掉邊 ,每種情況下的最大值
若爲t;絕對值最大
最大數+最大數
最小數+最小數
若爲x
正數*正數
負數*負數
正數*負數
負數*正數
然後對於dpmax[][] 在p[] 存下可能的辦法 詳見代碼。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#define LL long long
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
#define inf 0x3f3f3f3f

int dpmin[110][110],dpmax[110][110];
//   第 i 和 j 個數運算
int v[110];
char op[110];
int main()
{   freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    int n;
    int i,j,k;
    mem(dpmin,inf);
    mem(dpmax,-inf);
    scanf("%d",&n);
    getchar();
    for(i=1;i<=n;i++){
            scanf("%c %d",&op[i],&v[i]);
            getchar();
            op[i+n]=op[i];
            v[i+n]=v[i];
        }
        for(i=1;i<=2*n;i++)
          {
              dpmax[i][i]=dpmin[i][i]=v[i];
              if(i<2*n){
                if(op[i+1]=='t')
                  dpmax[i][i+1]=dpmin[i][i+1]=v[i]+v[i+1];
                else
                  dpmax[i][i+1]=dpmin[i][i+1]=v[i]*v[i+1];
              }
          }

        for(k=3;k<=n;k++) // k區間的長度
            for(i=1;i+k-1<=2*n;i++){
                    dpmax[i][i+k-1]=-inf;
                    dpmin[i][i+k-1]=inf;
                for(j=i;j<i+k-1;j++){
                    if(op[j+1]=='t'){
                       dpmax[i][i+k-1]=max(dpmax[i][i+k-1],dpmax[i][j]+dpmax[j+1][i+k-1]);

                       dpmin[i][i+k-1]=min(dpmin[i][i+k-1],dpmin[i][j]+dpmin[j+1][i+k-1]);
                    }
                    else{
                       dpmax[i][i+k-1]=max(dpmax[i][i+k-1],dpmax[i][j]*dpmax[j+1][i+k-1]);
                       dpmax[i][i+k-1]=max(dpmax[i][i+k-1],dpmin[i][j]*dpmin[j+1][i+k-1]);
                       dpmax[i][i+k-1]=max(dpmax[i][i+k-1],dpmax[i][j]*dpmin[j+1][i+k-1]);
                       dpmax[i][i+k-1]=max(dpmax[i][i+k-1],dpmin[i][j]*dpmax[j+1][i+k-1]);

                       dpmin[i][i+k-1]=min(dpmin[i][i+k-1],dpmax[i][j]*dpmax[j+1][i+k-1]);
                       dpmin[i][i+k-1]=min(dpmin[i][i+k-1],dpmax[i][j]*dpmin[j+1][i+k-1]);
                       dpmin[i][i+k-1]=min(dpmin[i][i+k-1],dpmin[i][j]*dpmax[j+1][i+k-1]);
                       dpmin[i][i+k-1]=min(dpmin[i][i+k-1],dpmin[i][j]*dpmin[j+1][i+k-1]);
                    }

                }
            }
        int p[55],cnt=0;
        LL ans=-inf;

        for(i=1;i<=n;i++){
            if(dpmax[i][i+n-1]>ans){
                ans=dpmax[i][i+n-1];
                cnt=0;
                p[cnt]=i;
            }else  if(dpmax[i][i+n-1]==ans){
                p[++cnt]=i;
            }
        }
        printf("%I64d\n",ans);
        for(i=0;i<cnt;i++)
            printf("%d ",p[i]);
        printf("%d\n",p[i]);
 return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章