2017.10.10 (貪心+二分答案+DP)
*56學長們出的題,做的時候覺得好難QAQ,講完題後自己又打了一遍,開始懷疑起自己的智商T^T。。。好吧就這樣吧 _ψ(‥ )
注:題目的資料範圍以資料範圍及提示裡的為準。
t1:
剛開始做時感覺好像不是很難,因此下決心一定要把這道題做出來!
對於除灰色顏料外的其他顏料,考慮統計一個max,計算一下至少需要多少顏料套裝,再對剩餘的顏料看看還至少還要多少個顏料套裝可以把所需的灰色顏料配出來。
問題的關鍵就是怎麼配。
要知道不同的配法可以決定最後配出灰色顏料的多少。
例如我有4個套裝,每個套裝有四種顏色,我可以:
最後共湊出灰色顏料 200ml 剩餘 200 ml;
我也可以:
最後共湊出灰色顏料 250ml 剩餘50ml;
經過一系列的舉例子(?),我們發現如果用來配的單位顏料越少,顏料就越不容易浪費,答案就會更優。(PS:我考試時當然沒想到,莫名打了個二分加判斷水了40)
既然單位顏料越少越優,反正資料範圍小,乾脆1ml1ml地配吧!
開一個優先佇列,把剩餘的顏料統統丟進堆裡。每次彈出最多的三種顏料,每種-1,灰色顏料數+1,再丟進去,這樣可以保證用的套裝更少,答案更優。如果當前彈出的<=0,就把ans++,同時每種顏料數+50ml。
這樣還是有些麻煩,不如倒過來想。
每次都把所需的1ml灰色顏料轉為當前最少的三種顏料,儘量使每種顏料所需的數量相近,減少浪費。
最後算出所需的顏料套裝數。
正解( • ̀ω•́ )✧!
程式碼:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
int N,maxn,cnt,ans;
int a[110];
priority_queue<int,vector<int>,greater<int> > q;
int ask()
{
int b=q.top();
q.pop();
return b;
}
int main()
{
freopen("painter.in","r",stdin);
freopen("painter.out","w",stdout);
scanf("%d",&N);
while(N!=0)
{
maxn=0,cnt=0;
for(int i=1;i<=N;++i)
{
scanf("%d",&a[i]);
q.push(a[i]);
}
scanf("%d",&a[N+1]);
while(a[N+1])
{
int x=ask(),y=ask(),z=ask();
++x,++y,++z;
--a[N+1];
q.push(x),q.push(y),q.push(z);
}
while(!q.empty())
{
a[++cnt]=q.top();
q.pop();
maxn=max(maxn,a[cnt]);
}
ans=maxn/50;
if(maxn%50!=0) ++ans;
printf("%d\n",ans);
scanf("%d",&N);
}
return 0;
}
t2:
一看到最小距離最大化第一反應二分。因為是在矩陣中求到目標點的距離,所以選擇bfs。另外注意矩陣中,兩點間的曼哈頓距離就是從一個點到另一點的路徑長度。
正解:二分最小距離,bfs判斷能否走到目標點,true就調整二分下界,把距離往大處二分,不然就調整上界。因為二分要多次bfs時判斷曼哈頓距離,而兩點間的距離其實是固定不變的。所以我們先對每個敵人跑一遍bfs,處理出每個點到距離最近的敵人的距離。(並不會TLE)
考試時我是直接暴力判斷那個點走或不走,下來又調了調,可以得70分。
ac程式碼:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int N,X,Y,X1,Y1,x2,y2,ans;
int enemy[1010][1010];
int x[4]={0,1,0,-1},
y[4]={1,0,-1,0};
bool used[1010][1010];
struct maple{
int x,y,d;
}e[1010],h[1000010];
bool bfs(int n)
{
memset(used,0,sizeof(used));
int head=0,tail=1;
h[tail].x=X1,h[tail].y=Y1;
h[tail].d=0;
used[X1][Y1]=1;
if(enemy[X1][Y1]<n) return false;
if(enemy[x2][y2]<n) return false;
while(head<tail)
{
++head;
for(int i=0;i<=3;++i)
{
int xx=h[head].x+x[i],yy=h[head].y+y[i];
if(xx>=0&&xx<X&&yy>=0&&yy<Y&&!used[xx][yy]&&enemy[xx][yy]>=n)
{
used[xx][yy]=1;
h[++tail]=(maple){xx,yy,h[head].d+1};
if(xx==x2&&yy==y2)
{
ans=h[head].d+1;
return true;
}
}
}
}
return false;
}
void Bfs(int sx,int sy)
{
int head=0,tail=1;
memset(used,0,sizeof(used));
h[tail]=(maple){sx,sy,0};
used[sx][sy]=1;
while(head<tail)
{
++head;
for(int i=0;i<=3;++i)
{
int xx=h[head].x+x[i],yy=h[head].y+y[i];
if(xx>=0&&xx<X&&yy>=0&&yy<Y&&!used[xx][yy])
if(enemy[xx][yy]>h[head].d+1)
{
enemy[xx][yy]=min(enemy[xx][yy],h[head].d+1);
used[xx][yy]=1;
h[++tail]=(maple){xx,yy,enemy[xx][yy]};
}
}
}
}
int main()
{
freopen("escape.in","r",stdin);
freopen("escape.out","w",stdout);
scanf("%d%d%d",&N,&X,&Y);
scanf("%d%d%d%d",&X1,&Y1,&x2,&y2);
memset(enemy,63,sizeof(enemy));
for(int i=1;i<=N;++i)
{
scanf("%d%d",&e[i].x,&e[i].y);
enemy[e[i].x][e[i].y]=-1;
}
for(int i=1;i<=N;++i)
Bfs(e[i].x,e[i].y);
int l=0,r=2010;
while(l+1<r)
{
int mid=(l+r)>>1;
if(bfs(mid)) l=mid;
else r=mid;
}
printf("%d %d",l,ans);
return 0;
}
t3:
本來以為是數學題,講了題以後發現竟然是一個類似揹包的dp。。。
因為有負數,所以設天平的初始平衡度為7500(20個重25的砝碼掛在一個距離天平中央的距離為15的鉤子上, 20* 25 * 15)。
令 dp[i][j]為當前掛上了第i個砝碼後天平的平衡度為j時的方案數。
則 dp[i][j]=∑dp[i-1][j-c[k]*w[i]].
最後輸出dp[g][7500].
程式碼:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int C,G,maxn1,maxn2;
int c[25],w[25];
int dp[25][15010];
int main()
{
freopen("balance.in","r",stdin);
freopen("balance.out","w",stdout);
scanf("%d%d",&C,&G);
for(int i=1;i<=C;++i)
scanf("%d",&c[i]);
for(int i=1;i<=G;++i)
scanf("%d",&w[i]);
dp[0][7500]=1;
for(int i=1;i<=G;++i)
for(int k=1;k<=C;++k)
for(int j=w[i]*c[k];j<=15000;++j)
dp[i][j]+=dp[i-1][j-w[i]*c[k]];
printf("%d",dp[G][7500]);
return 0;
}
ヾノ≧∀≦)o