喜聞樂見,test續集。
來上題。
1.Hello
題目描述
Alice 和 Bob 有一個長度爲2n的數。現在他們要在這個數字上玩遊戲。他們分別要從 2n 個位中取出 n 個位組成自己的幸運值。每一回合,Alice 或 Bob 把數字最左邊的那一位拿來放在自己幸運值的最末位。在第 i 輪操作過後,被選取的數位(原數的第i位)會從原數中消失。現在 Alice 和 Bob 想要使得他們兩個幸運值的和儘可能大。請求出這個值。
輸入格式
第一行一個整數 n ,意爲幸運值的長度。
第二行一個長度爲 2n 的數字,表示原數。
輸出格式
一個整數爲幸運值的和的最大值。
樣例數據
輸入
2
1234
輸出
46
備註
【樣例說明】
Alice 取 1、2 位。Bob 取 3、4 位。幸運值分別爲 12 和 34 ,和爲 46 。
【數據範圍】
對 30% 的輸入數據 : n≤10;
對 100% 的輸入數據 : n≤18,原數與幸運值均允許前綴 0 的存在。
用動歸。
對於一個人的倒着數第i位對於最終答案的貢獻爲a[i]*10^i。
f[i][j]表示Alice取了前i位,Bob取了前j位的狀態下,ans的最大值。
f[i][j]可以轉移到f[i][j+1]或者f[i+1][j],最終輸出f[n][n]。
如下。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
int n;
long long b[101],v[21],f[21][21],pre[21][21];
char a[101];
int main()
{
//freopen("hello.in","r",stdin);
//freopen("hello.out","w",stdout);
memset(f,255,sizeof(f));
scanf("%d%s",&n,a+1);
v[0]=1;
f[0][0]=0;
for(int i=1;i<=18;i++)
v[i]=v[i-1]*10; //對我就是貢獻的那個10^i
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
{
if(i!=n&&f[i][j]+v[n-i-1]*(a[i+j+1]-'0')>f[i+1][j])
{
f[i+1][j]=f[i][j]+v[n-i-1]*(a[i+j+1]-'0');
pre[i+1][j]=0;
} //兩種可能的狀態轉移方程。
if(j!=n&&f[i][j]+v[n-1-j]*(a[i+j+1]-'0')>f[i][j+1])
{
f[i][j+1]=f[i][j]+v[n-1-j]*(a[i+j+1]-'0');
pre[i][j+1]=1;
}
}
cout<<f[n][n];
return 0;
}
2.Rect
題目描述
給定一個由數字(0-9)構成的字符串 s 。我們可以由此定義出 size(s) * size(s) 大小的矩陣 b ,其中 b[i][j] = s[i] * s[j] ;請問在這個矩陣 b 中,有多少子矩形滿足其中的 b[i][j] 的和爲另一個給定的數字 a 。
輸入格式
第一行一個整數 a 。
第二行字符串 s 。
輸出格式
輸出一個整數表示滿足條件的子矩形數。
樣例數據
輸入
10
12345
輸出
6
備註
【樣例說明】
b矩陣爲:
01 02 03 04 05
02 04 06 08 10
03 06 09 12 15
04 08 12 16 20
05 10 15 20 25
和爲10的子矩形有:
(1)
01 02 03 04
(2)
01
02
03
04
(3)
04 06
(4)
04
06
(5)
10
(6)
10
以上共六個。
【數據範圍】
對 10% 的輸入數據 :size(s)≤10 ;
對 30% 的輸入數據 :size(s)≤100 ;
對 100% 的輸入數據 :0≤a≤1000000000,size(s)≤4000 。
由於b[i][j] = s[i] * s[j],則矩陣的第一行和第一列爲原序列,且下面所有矩陣的和都是第一行子矩陣的和的倍數,也就是第一行的子矩陣和第一列的子矩陣的乘積。即要枚舉出符合提議的矩陣,應先找第一行的數,然後用找符合a/sum[x1…x2](指第一行從x1到x2的和)=sum[y1…y2](指第一列從y1到y2的和)的子矩陣的值分別有多少個,再把兩個數乘起來累加。
但如果a=0,則上述方法不成立,當sum[x1…x2]=0時,sum[y1…y2]可以取任何值。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
int n,len;
int a[45000],cnt[45000];
long long ans;
char s[45000];
int main()
{
//freopen("rect.in","r",stdin);
//freopen("rect.out","w",stdout);
scanf("%d%s",&n,s+1);
len=strlen(s+1);
for(int i=1;i<=len;i++)
a[i]=a[i-1]+s[i]-'0'; //第一排各種各樣的和
for(int i=1;i<=len;i++)
for(int j=i;j<=len;j++)
cnt[a[j]-a[i-1]]++; //每種因數出現次數
if(n!=0)
{
for(int i=1;i<=len;i++)
for(int j=i;j<=len;j++)
{
int t=a[j]-a[i-1];
if(t==0)
continue;
if(n%t||(n/t)>45000)
continue;
ans+=cnt[n/t];
}
}
else
{
for(int i=1;i<=len;i++)
for(int j=i;j<=len;j++)
{
int t=a[j]-a[i-1];
if(t==0)
ans+=len*(len+1)/2; //相乘
else
ans+=cnt[0];
}
}
cout<<ans<<endl;
return 0;
}
3.Shortest
題目描述
給定一張 n 個點的有向帶權完全圖,和一個數組 a[] ,請按順序刪除數組中的點,請求出在刪除點 a[i] 以前,所有未刪除點對之間的最短路的值的和。
輸入格式
第一行一個整數 n ,表示點數;
接下來 n 行,每行 n 個數構成鄰接矩陣,描述每條邊的權值,保證 i 號點到 i 號點的權值爲 0 ;
最後一行 n 個小於等於 n 的不同的數,描述數組 a[i]。
輸出格式
輸出 1 行 n 個數,表示答案。
樣例數據
輸入
4
0 3 1 1
6 0 400 1
2 4 0 1
1 1 1 0
4 1 2 3
輸出
17 23 404 0
備註
【數據範圍】
對 30% 的輸入數據 :1≤n≤10 ;
對 100% 的輸入數據 :1≤n≤500;0<權值≤100000 。
如果用最短路的話是會超時的呦,不論是Dijkstra還是SPFA。
這是考試時血與淚的教訓,一定要記得算時間複雜度和空間複雜度。
當然我們可以換種方式考慮。
把刪點的行爲看作是逆向加點,即往裏面不斷加點並更新答案。
可以用Floyed的思想。
因爲並不是每個點的最短路都會更新,一共只有三種情況。
一是所加點到已加的所有點的路徑的更新。
二是已加的所有點到所加點的路徑的更新。
三是已加點到已加點中途經過所加點的路徑的更新。
如代碼。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
using namespace std;
int n;
int f[510][510],a[510];
long long ans[510];
int main()
{
//freopen("shortest.in","r",stdin);
//freopen("shortest.out","w",stdout);
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>f[i][j];
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=n;i;i--)
{
for(int j=n;j>i;j--)
for(int k=n;k>i;k--)
{
f[a[i]][a[j]]=min(f[a[i]][a[j]],f[a[i]][a[k]]+f[a[k]][a[j]]); //第一種情況
f[a[j]][a[i]]=min(f[a[j]][a[i]],f[a[j]][a[k]]+f[a[k]][a[i]]); //再反着來一遍,第二種情況
}
for(int j=n;j>i;j--)
for(int k=n;k>i;k--)
f[a[j]][a[k]]=min(f[a[j]][a[k]],f[a[j]][a[i]]+f[a[i]][a[k]]); //所加點爲中途的點,第三種情況
for(int j=n;j>=i;j--)
for(int k=n;k>=i;k--)
ans[i]+=f[a[j]][a[k]];
}
for(int i=1;i<=n;i++)
cout<<ans[i]<<" ";
return 0;
}
嗯,就是這樣。
來自2017.7.15
——我認爲return 0,是一個時代的終結。