7.21 暑假集訓——動態規劃篇(二)
共17道題目,其中最後五題是拓展題,數位dp和插頭dp沒有寫出來
按照過題的順序記錄, 可以從下往上看~
E-Coins
題意: 告訴你 n 種錢幣的面值和數目,求【1,m】之間,有多少數目是用這些錢幣能恰好湊出的?
思路:經典裸題,參考《挑戰程式設計》P74
注意: 記憶體限制,可以參考書上的利用奇偶性減少記憶體消耗
B-免費餡餅
題意: 現在給這條小徑如圖示上座標:
————————————
0 1 2 3 4 5 6 7 8 9 10
假設餡餅都掉落在0-10這11個位置。gameboy每秒種只有在移動不超過一米的範圍內接住墜落的餡餅。開始時gameboy站在5這個位置,因此在第一秒,他只能接到4,5,6這三個位置中其中一個位置上的餡餅。問gameboy最多可能接到多少個餡餅?(假設他的揹包可以容納無窮多個餡餅)
思路:
J-Humble Numbers
題意: 只由2,3,5,7相乘得到的數字為Humble Numbers,如1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 24, 25, 27, …
求第n個Humber Numbers
思路: 參考紫皮 第十章
注意: G++和C++ 編譯上的不同
D-How to type
思路: 定義狀態d[i][0]和d[i][1]分別表示第i個字元大寫和小寫所需的最小運算元,遞推一下
A- Big event in HDU ****
思路: 要轉換一下思路,轉換成求0-sum 值中用有n人的m個學院最多湊出多少個數,然後找出這些數中最靠近sum/2的數即可
code:http://paste.ubuntu.com/25197810/
L-How many ways
題意: 這是一個簡單的生存遊戲,你控制一個機器人從一個棋盤的起始點(1,1)走到棋盤的終點(n,m)。遊戲的規則描述如下:
1.機器人一開始在棋盤的起始點並有起始點所標有的能量。
2.機器人只能向右或者向下走,並且每走一步消耗一單位能量。
3.機器人不能在原地停留。
4.當機器人選擇了一條可行路徑後,當他走到這條路徑的終點時,他將只有終點所標記的能量。
我們的問題是機器人有多少種方式從起點走到終點。這可能是一個很大的數,輸出的結果對10000取模。
code:
Beans ****
Doint homework again
樹形dp ****
題意 公司裡上級和下級形成一棵樹,每個人期待參加派對的程度都不同,求在直接上級和下級不同時出現在派對的情況下,最大的期待總值
思路: 先是怎麼儲存樹,我用的vector
然後分別用0,1儲存這個人不參加和參加派對時的最大期待值
code: http://paste.ubuntu.com/25198008/
G&H 求最大相同字元的子矩陣面積 *****
思路: 本來是很不會矩陣求面積的…… 後來居然看著看著 看通暢了……
記錄每一列的最大深度,求出到最左和最右的距離,用這個面積來更新ans
code: http://paste.ubuntu.com/25198040/
因為最後兩題想直接給出程式碼,所以把兩題沒有做出來的放到上面來
數位dp
題意: 求一個區間內滿足條件的數有多少個,條件是該數可以寫成k個b的不同次方的和
參考: 劉聰 《淺談數位類統計問題》
插頭dp HDU P1693
題意:
呃…… 略略略
狀壓dp *********
思路: 先看資料範圍 1 ≤ M ≤ 12; 1 ≤ N ≤ 12 可以狀壓沒有錯
然後列舉,d[i][j] 為第i行為j排列時 最大的排列數
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
int pic[15][15];
const int bit=(1<<14)-1;
LL d[15][bit+10];
int n,m;
const int mod=1e8;
bool able(int i,int j){ //判斷第i行 j種排列可行,1.沒有兩個1相鄰,2.沒有荒地種了1
int lim=0;
for(int k=0;k<m;k++)
lim=(lim<<1)+pic[i][k];
if(lim&j) return false;
int be=j&1;j>>=1;
while(j){
if(be==1&&(j&1)) return false;
be=j&1; j>>=1;
}
return true;
}
bool judge(int i,int j){ //判斷相鄰兩行的i,j排列是否可行,相鄰字元不都為1則可行
bool res=true;
for(int k=0;k<m;k++){
int n=i&1,m=j&1;
if(n==1&&m==1) res=false;
i>>=1,j>>=1;
}
return res;
}
void print(int j){ //輸出解
while(j){
printf("%d",j&1);
j>>=1;
}
cout<<endl;
}
int main()
{
//freopen("1.txt","r",stdin);
while(~scanf("%d%d",&n,&m)){
memset(pic,0,sizeof(pic));
memset(d,0,sizeof(d));
for(int i=0;i<n;i++)
for(int j=0;j<m;j++){
int t;
scanf("%d",&t);
pic[i][j]=!t;
}
for(int i=0;i<(1<<m);i++)
if(able(0,i)) d[0][i]=1;
for(int i=1;i<n;i++){
for(int j=0;j<(1<<m);j++){
if(!able(i,j)) {d[i][j]=0;continue;} //第i行可以取j排列
for(int k=0;k<(1<<m);k++){
if(judge(j,k)&&able(i-1,k)) d[i][j]=(d[i][j]+d[i-1][k])%mod; //i-1行可以取k排列,i行以取j排列時 可以取的排列數
}
}
}
LL ans=0;
for(int j=0;j<(1<<m);j++) ans=(ans+d[n-1][j])%mod;
printf("%lld\n",ans%mod);
}
return 0;
}
區間dp **********
思路: 給出一個多邊形,已知每一個頂點的座標,告訴你每一刀的消耗與座標的關係式 costi, j = |xi + xj| * |yi + yj| % p 。求將這個多邊形分為n-2個三角形所需要的最小消耗
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
struct Point{ //定義點的結構體
int x,y;
Point(int x=0,int y=0):x(x),y(y) {};
};
bool operator < (const Point& a,const Point& b){ //兩種排序
return (a.x!=b.x) ? a.x<b.x: a.y>b.y;
}
bool cmp (const Point &a,const Point &b){
return (a.x!=b.x) ? a.x>b.x:a.y<b.y;
}
vector<Point> vec; //點的向量集
const int INF=1e9;
int n,q;
int d[310][310];
int c[310][310];
//已知三角形三個頂點的座標,求面積,利用公式
double area(int i,int j,int k){
Point a=vec[i],b=vec[j],c=vec[k];
return (fabs(a.x*(b.y-c.y)+b.x*(c.y-a.y)+c.x*(a.y-b.y))/2.0);
}
//判斷點是不是在已有的多邊形的內部,如果是則為凹多變形,理解
bool judge(int i,int j,int k){
for(int t=0;t<n;t++){
if(t==i||t==j||t==k) continue;
if(area(i,j,k)==area(i,j,t)+area(i,k,t)+area(j,k,t)) return false;
}
return true;
}
// 用動態規劃求最優解,重點d[i][j]=min(d[i][j],d[i][k]+d[k][j]+c[i][k]+c[k][j]);
bool solve(){
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++){
if(i-j==1||j-i==1) c[i][j]=c[j][i]=0;
else {
c[i][j]=c[j][i]=(abs(vec[i].x+vec[j].x)*abs(vec[i].y+vec[j].y))%q;
}
}
for(int i=n-2;i>=0;i--)
for(int j=i+1;j<n;j++){
if(j==i+1) d[i][j]=0;
else{
d[i][j]=INF;
for(int k=i+1;k<j;k++){
if(!judge(i,j,k)) return false; //判斷凸邊形
d[i][j]=min(d[i][j],d[i][k]+d[k][j]+c[i][k]+c[k][j]);
}
}
}
return true;
}
//給點排序,使點按逆時針方向形成一個多邊形
void Sort(){
sort(vec.begin(),vec.end());
int lx,ly,rx,ry;
lx=vec[0].x,ly=vec[0].y;
rx=vec[n-1].x,ry=vec[n-1].y;
double k=double (ly-ry)/double (lx-rx);
double b=double(ly)-k*lx;
vector<Point> vec2;
vec2.clear();
for(int i=0;i<n;i++)
{
if(vec[i].x*k-vec[i].y+b<=0){
vec2.push_back(vec[i]);
vec[i].x=vec[i].y=-INF;
}
}
sort(vec.begin(),vec.end(),cmp);
for(int i=0;i<n;i++)
if(vec[i].x>=lx) vec2.push_back(vec[i]);
swap(vec,vec2);
}
int main()
{
//freopen("1.txt","r",stdin);
while(~scanf("%d%d",&n,&q)){
vec.clear();
for(int i=0;i<n;i++){
int x,y; scanf("%d%d",&x,&y); //讀取每個點
vec.push_back(Point(x,y));
}
Sort();
memset(d,-1,sizeof(d));
bool jud=solve();
if(!jud){
printf("I can't cut.\n"); continue;
}
printf("%d\n",d[0][n-1]);
}
return 0;
}