前言
這次考試整體來言還是不錯的,但是還是存在一些問題,就第三題而言考試時並沒有深入思考,然後暴力還掛了。
試題
【道路修建】(Road.pas/c/cpp Time:1.2s Memory:256M)
【問題描述】
現在在 LJY 星球上有 N 個城市。LJY 爲了使各個國家的經濟發展,決定在
各個國家之間建設雙向道路使得國家之間連通。但是 LJY 很吝嗇,只願意修建
恰好 n–1 條雙向道路。每條道路的修建都要付出一定的費用,這個費用等於道
路長度乘以道路兩端的國家個數之差的絕對值。例如,在下圖中,虛線所示道路
兩端分別有 2 個、4 個國家,如果該道路長度爲 1,則費用爲 1×|2 – 4|=2。圖
中圓圈裏的數字表示國家的編號。
由於國家的數量十分龐大,道路的建造方案有很多種,同時每種方案的修建
費用難以用人工計算,LJY 決定找人設計一個軟件,對於給定的建造方案,計算
出所需要的費用。請你幫助 LJY 設計一個這樣的軟件。
【輸入】
輸入文件名爲 Road.in。
輸入第一行爲一個正整數 N,代表總的點數的個數。
下接 N-1 行,每行三個正整數 ai、bi、ci,代表有一條長度爲 ci 的雙向道路
連接 ai 和 bi 兩個國家之間。
【輸出】
輸出文件名爲 Road.txt。
輸出一行一個正整數,爲修建所有道路的總費用。
【輸入輸出樣例】
Road.in
6
1 2 1
1 3 1
1 4 2
6 3 1
5 2 1
Road.txt
20
【數據範圍】
對於 40%的數據,
對於 70%的數據,
對於 100%的數據,
不允許開開關。
【題解】
原題竟然是NOI2011,但是這道題真的很水
我們可以直接以某個點爲根節點,然後將對這個樹進行一次搜索遍歷。那麼顯然我們可以算出每個以每個點爲根節點的子樹包含的點數,簡單的記爲
那麼,連接第I個點的樹邊連接城市之差就是
那麼進行一次簡單的統計即可。
但是本題直接dfs搜索會爆掉棧空間,但是測評時全機房決定還是開大棧空間(似乎大部分人打的dfs,我當時怕爆棧就打了bfs(雖然有點慢))。
【代碼】
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
typedef long long LL;
const int maxn = 2000000+20;
int point[maxn],nxt[maxn],g[maxn],val[maxn];
int p[maxn],f[maxn],fx[maxn],s[maxn];
bool v[maxn];
int n,tot;
inline int read() {
int in=0,f=1;
char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())
if(ch=='-')
f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())
in=in*10+ch-'0';
return in*f;
}
inline void add_edge(int x,int y,int z) {
point[++tot]=y;nxt[tot]=g[x];g[x]=tot;val[tot]=z;
}
int main() {
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
n=read();
for(int i=1;i<n;i++) {
int x=read(),y=read(),z=read();
add_edge(x,y,z);add_edge(y,x,z);
}
p[1]=1;v[1]=true;
for(int i=0,j=1;i<=j;i++) {
for(int k=g[p[i]];k!=0;k=nxt[k]) {
if(v[point[k]]==false) {
v[point[k]]=true;fx[point[k]]=val[k];
f[point[k]]=p[i];p[++j]=point[k];
}
}
}
for(int i=n;i>=1;i--) ++s[p[i]],s[f[p[i]]]+=s[p[i]];
LL ans=0;
for(int i=1;i<=n;i++) {
ans+=((LL)fx[i])*(abs((LL)n-((s[i])<<1)));
}
printf("%lld\n",ans);
return 0;
}
【迷宮巡迴】(maze.pas/c/cpp Time:1s Memory:256M)
【問題描述】
現在有一個 N*M 的迷宮,LJY 處在第一行第一列這個位置,也就是起點上,迷宮的補
給點在(N,M)。這個迷宮中的每個格子都有一個激情度,也就是說,LJY 走到這個格子
便可以獲得這個格子上的激情度。但是,走過一遍的格子便沒有激情度了。所以,LJY 爲了
獲得最大的激情度,便不希望走到同一個格子上,除了起點。首先,LJY 會從起點走到補給
點,此時,LJY 只能向下或者向右運動到相鄰的格子。到了補給點之後,LJY 又從補給點開
始,向上或者向左運動到相鄰的格子,一直到起點。當然,萬一 LJY 走到了迷宮之外,他
就掛定了,所以他絕對不會走到迷宮之外的。現在 LJY 想知道,自己巡迴一遍迷宮之後,
能獲得的最大的激情度有多少?
【輸入】
輸入文件名爲 maze.in。
輸入一行兩個正整數 N 和 M,代表迷宮的行數和列數。
下接一個 N 行 M 列的矩陣,其中第 I 行第 J 列的數代表遊歷這個格子的激情度。
【輸出】
輸出文件名爲 maze.txt。
輸出一行一個正整數,代表 LJY 巡迴一遍迷宮能獲得的最大的激情度。
【輸入輸出樣例】
maze.in
3 3
0 3 9
2 8 5
5 7 0
maze.txt
34
【數據範圍】
對於 30%的數據,
對於 100%的數據,
保證每個格子的激情度均爲不大於 100 的非負整數。
【題解】
剛開始打了個暴力,把題目大概意思搞清楚了。
然後發現人都只能向下和向右走。
所以我們可以用dp的思想,用一個三維數組
【代碼】
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
typedef long long LL;
const int size = 50+5;
int f[size<<1][size][size],w[size][size];
int n,m;
inline int read() {
int in=0,f=1;
char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())
if(ch=='-')
f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())
in=in*10+ch-'0';
return in*f;
}
namespace my_dp{
inline int cal(int x,int y) {
x-=y;
if (x<=0||x>n) return -1;
return w[x][y];
}
inline void work() {
f[3][1][2]=w[1][1]+w[1][2]+w[2][1];
for(int i=3;i<=m+n;i++) {
for(int x=1;x<=m;x++) {
for(int y=x+1;y<=m;y++) {
// dp_start-------------------------
int sta=cal(i,x),stb=cal(i,y);
if(sta<0 or stb<0) continue;
// dp_1-------------------------
sta=cal(i+1,x);stb=cal(i+1,y);
if(sta>=0 and stb>=0 and sta+stb+f[i][x][y]>f[i+1][x][y])
f[i+1][x][y]=f[i][x][y]+sta+stb;
// dp_2-------------------------
sta=cal(i+1,x);stb=cal(i+1,y+1);
if(sta>=0 and stb>=0 and sta+stb+f[i][x][y]>f[i+1][x][y+1])
f[i+1][x][y+1]=f[i][x][y]+sta+stb;
// dp_3-------------------------
sta=cal(i+1,x+1);stb=cal(i+1,y);
if(sta>=0 and stb>=0 and sta+stb+f[i][x][y]>f[i+1][x+1][y])
f[i+1][x+1][y]=f[i][x][y]+sta+stb;
// dp_4-------------------------
sta=cal(i+1,x+1);stb=cal(i+1,y+1);
if(sta>=0 and stb>=0 and sta+stb+f[i][x][y]>f[i+1][x+1][y+1])
f[i+1][x+1][y+1]=f[i][x][y]+sta+stb;
// dp_end-------------------------
}
}
}
printf("%d\n",f[n+m-1][m-1][m]+w[n][m]);
}
}
int main() {
freopen("maze.in","r",stdin);
freopen("maze.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
w[i][j]=read();
}
}
//my_bfs::print();
my_dp::work();
return 0;
}
【單詞矩陣】(twofive.pas/c/cpp Time:1s Memory:256M)
【問題描述】
LJY開始研究英語的單詞。對於包含字母A到Y各一次的單詞S,將其從上到下從左到右寫
在一個5*5的矩陣中,如單詞ADJPTBEKQUCGLRVFINSWHMOXY寫出來如下:
合法的
A D J P T
A D J P T
B E K Q U
B E G Q U
C G L R V
不合法的
C K L R V
F I N S W
F I N S W
H M O X Y
H M O X Y
若該矩陣滿足每一行每一列的字母都是字典序遞增的則滿足LJY對優美的要求,如上述
單詞就是優美的,而ADJPTBEGQUCKLRVFINSWHMOXY則不是(第二列不滿足要求)。
LJY將所有優美的單詞按字典序列出,從小到大編號1,2,……
請你完成以下兩種任務:
1. 給定一個優美的單詞,求其編號。
2. 給定一個編號,求對應的優美的單詞。
【輸入】
輸入文件名爲twofive.in。
輸入第一行一個字母,W表示任務1,N表示任務2。
若是任務1,第二行是一個優美的單詞,否則第二行是一個正整數,表示某個優美的單
詞的編號,保證該數不超過優美的單詞的總數。
【輸出】
輸出文件名爲twofive.txt。
輸出僅一行,若是任務1,輸出對應編號,否則輸出對應的優美的單詞
【輸入輸出樣例】
twofive.in
W
ABCDEFGHIJKLMNOPQRSUTVWXY
twofive.txt
2
twofive.in
N
20
twofive.txt
ABCDEFGHIJKLMNOPQSUWRTVXY
【數據範圍】
保證數據合法。保證數據有梯度。
【題解】
(來源於網絡)
以下敘述中,“單詞”均指合法單詞。
舉個例子說明:若爲單詞轉編碼,如求單詞ACF……的編碼,則設一累加器,先累加以AB開頭的單詞的個數,再累加以ACB開頭的單詞的個數(這個數爲0,但若已知6個字母的位置,B拐到了第2行,則可能不爲0),再累加以ACD開頭的單詞的個數,再累加以ACE開頭的單詞的個數……最後加1即得答案。若爲編碼轉單詞,如求第n個單詞,同樣設一累加器s,先累加以AB開頭的單詞的個數,若
現在的問題是:如何求出以某給定序列開頭的單詞的個數?這個問題是用記憶化搜索解決的。用
第一層遞歸安置字母A。因其位置已固定,故
第二層遞歸安置字母B。B的位置尚未固定,於是枚舉所有合法位置(合法位置指左、上方均已填有字母的位置,認爲第0行與第0列均已填滿。此例中爲12、21),分別進入第三層遞歸計算
第三層遞歸安置字母C。這層遞歸的過程與第一層遞歸類似。更深層遞歸的過程與第二層遞歸類似。若在某一次遞歸中,需要計算的f值已經算出,則不必再遞歸下去,直接退出即可。
【代碼】
#include<cstdio>
const char alphabet[]="ABCDEFGHIJKLMNOPQRSTUVWXY";
using namespace std;
int f[6][6][6][6][6],row[6],xpos[30],ypos[30];
int state[26][50][6];
char task,s[30],ans[30];
int snum[26];
int a,b,c,d,e,ANSWER=1;
inline int DP()
{
f[5][5][5][5][5]=1;
for (int sum=24;sum>=0;sum--)
for (int nows=1;nows<=snum[sum];nows++)
{
row[1]=state[sum][nows][1];
row[2]=state[sum][nows][2];
row[3]=state[sum][nows][3];
row[4]=state[sum][nows][4];
row[5]=state[sum][nows][5];
row[0]=5;
a=row[1];b=row[2];c=row[3];d=row[4];e=row[5];
f[a][b][c][d][e]=0;
if (!xpos[sum])
{
if (a<5) f[a][b][c][d][e]+=f[a+1][b][c][d][e];
if (b<a) f[a][b][c][d][e]+=f[a][b+1][c][d][e];
if (c<b) f[a][b][c][d][e]+=f[a][b][c+1][d][e];
if (d<c) f[a][b][c][d][e]+=f[a][b][c][d+1][e];
if (e<d) f[a][b][c][d][e]+=f[a][b][c][d][e+1];
}
else
{
if (row[xpos[sum]]<row[xpos[sum]-1]&&row[xpos[sum]]+1==ypos[sum])
{
++row[xpos[sum]];
f[a][b][c][d][e]+=f[row[1]][row[2]][row[3]][row[4]][row[5]];
}
}
}
return f[0][0][0][0][0];
}
inline void init()
{
for (int i1=0;i1<6;i1++)
for (int i2=0;i2<=i1;i2++)
for (int i3=0;i3<=i2;i3++)
for (int i4=0;i4<=i3;i4++)
for (int i5=0;i5<=i4;i5++)
{
int sum=i1+i2+i3+i4+i5;
++snum[sum];
state[sum][snum[sum]][1]=i1;
state[sum][snum[sum]][2]=i2;
state[sum][snum[sum]][3]=i3;
state[sum][snum[sum]][4]=i4;
state[sum][snum[sum]][5]=i5;
}
}
void num2word()
{
for (int i=0;i<25;i++)
{
xpos[i]=0;ypos[i]=0;
}
int n;
scanf("%d",&n);
for (int i=1;i<6;i++)
for (int j=1;j<6;j++)
for (int ch=0;ch<25;ch++)
if (!xpos[ch])
{
xpos[ch]=i;ypos[ch]=j;
int temp=DP();
if (n>temp) n-=temp;
else break;
xpos[ch]=0;ypos[ch]=0;
}
for (int i=0;i<25;i++)
ans[(xpos[i]-1)*5+ypos[i]-1]=alphabet[i];
printf("%s\n",&ans);
}
int word2num()
{
char tmp;
scanf("%c",&tmp);
for (int i=1;i<6;i++)
for (int j=1;j<6;j++)
{
scanf("%c",&tmp);
int nowch=tmp-65;
for (int ch=0;ch<nowch;ch++)
if (!xpos[ch])
{
xpos[ch]=i;ypos[ch]=j;
ANSWER+=DP();
xpos[ch]=0;ypos[ch]=0;
}
xpos[nowch]=i;ypos[nowch]=j;
}
return ANSWER;
}
int main()
{
freopen("twofive.in","r",stdin);
freopen("twofive.out","w",stdout);
init();
scanf("%c",&task);
if (task=='N') num2word();
else printf("%d\n",word2num());
return 0;
}
總結
這次考試題目除了最後一題外整體還是不太難的,最後一題的確是一道好題,而且讓我對dp的思想似乎有了一種新的突破,但是還需繼續努力。