【NOIP2018模擬賽2018.10.27】
小貓爬山
【題目描述】:
Freda和rainbow飼養了N只小貓,這天,小貓們要去爬山。經歷了千辛萬苦,小貓們終於爬上了山頂,但是疲倦的它們再也不想徒步走下山了(嗚咕>_<)。
Freda和rainbow只好花錢讓它們坐索道下山。索道上的纜車最大承重量為W,而N只小貓的重量分別是C1、C2……CN。當然,每輛纜車上的小貓的重量之和不能超過W。每租用一輛纜車,Freda和rainbow就要付1美元,所以他們想知道,最少需要付多少美元才能把這N只小貓都運送下山?
【輸入描述】:
第一行包含兩個用空格隔開的整數,N和W。
接下來N行每行一個整數,其中第i+1行的整數表示第i只小貓的重量Ci。
【輸出描述】:
輸出一個整數,最少需要多少美元,也就是最少需要多少輛纜車。
【樣例輸入】:
5 1996
1
2
1994
12
29
【樣例輸出】:
2
【時間限制、資料範圍及描述】:
時間:1s 空間:128M
對於100%的資料,1<=N<=18,1<=Ci<=W<=10^8。
題意就是有n只貓,分別重wi,然後要裝進若干個承重為w的纜車裡,問最少用的纜車數。
正解應該是迭代加深深搜,但是lz貪心直接水過100分。。。
深搜不說了,貪心就是儘量讓體重最小的跟體重最大的坐在一起,能裝就裝。(感覺一般現實生活中我們也會這樣處理這種問題吧,但是前輩們說這種貪心不對,但我覺得沒有更優地做法了呀。。望知道的dalao告訴蒟蒻qwq)
程式碼5分鐘寫出來:
#include<bits/stdc++.h> using namespace std; #define ll long long #define pt putchar #define gc getchar #define ex pt('\n') #define ko pt(' ') const int MAXN = 20; int n,w; int v[MAXN],vis[MAXN]; int ans = 0; void in(int &x) { int num = 0,f = 1; char ch = gc(); while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();} while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();} x = num*f; } void out(int x) { if(x < 0) x = -x,pt('-'); if(x > 9) out(x/10); pt(x % 10 + '0'); } int main() { in(n); in(w); for(int i = 1;i <= n;i++) in(v[i]); sort(v+1,v+1+n); for(int i = 1;i <= n;i++) { if(vis[i]) continue; ans++; int now = v[i]; vis[i] = 1; for(int j = n;j >= i;j--) if(now + v[j] <= w && !vis[j]) vis[j] = 1,now += v[j]; } out(ans); return 0; }
得分
這道題老師沒放出來題目orz
大概題意是:一位同學要在T時間內做n道題中的全部或是部分,每道題有一個題目難度ci,需要時間ti,在剩餘時間x時開始做第i道題並且能做完的話,就可以得到x*ci的分,問可以得到的最大得分。
很明顯是個01揹包問題,但是要注意的是dp順序必須按 價效比(ci/ti :cj/tj) 從大到小來進行,不然答案無法被正確更新(至於為什麼,我也不是很明白,但是經過模擬發現若順序不對,就會導致答案沒有被更新出來,似乎是被前面的不優的東西擠掉了空間)
因為除法很麻煩,還要處理浮點數,所以比較函式寫成 ci*tj > cj*ti 就好。
哦對了,我的方程意思是f[j]代表當前剩餘時間,最後答案明顯取個最大(也許就是f[0]?),由於是01揹包只能限制f從上一階段逆向列舉過來,保證只轉移一次。
但是呀我這裡方程意思是剩餘時間,那麼相對於揹包裝進j來說的逆向是從已經裝了m進行逆向列舉轉移,同時可以想到對於剩餘時間來說,就應該從0開始轉移到m才算是逆向,故方程如下程式碼。
程式碼:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt putchar
#define gc getchar
#define ex pt('\n')
#define ko pt(' ')
const int MAXN = 3e3 + 5;
int n,m;
struct score
{
int t,c;
bool operator < (const score &one) const
{
return c*one.t > one.c*t;
}
}s[MAXN];
int f[10005];
int ans = 0;
void in(int &x)
{
int num = 0,f = 1; char ch = gc();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
x = num*f;
}
void out(int x)
{
if(x < 0) x = -x,pt('-');
if(x > 9) out(x/10);
pt(x % 10 + '0');
}
int main()
{
in(n); in(m);
for(int i = 1;i <= n;i++) in(s[i].t),in(s[i].c);
sort(s+1,s+1+n);
for(int i = 1;i <= n;i++)
for(int j = s[i].t;j <= m;j++)
f[j-s[i].t] = max(f[j-s[i].t],f[j] + j*s[i].c);
for(int i = 0;i <= m;i++) ans = max(ans,f[i]);
out(ans);
return 0;
}
迷路
Description
windy在有向圖中迷路了。 該有向圖有 N 個節點,windy從節點 0 出發,他必須恰好在 T 時刻到達節點 N-1。 現在給出該有向圖,你能告訴windy總共有多少種不同的路徑嗎? 注意:windy不能在某個節點逗留,且通過某有向邊的時間嚴格為給定的時間。
Input
第一行包含兩個整數,N T。 接下來有 N 行,每行一個長度為 N 的字串。 第i行第j列為'0'表示從節點i到節點j沒有邊。 為'1'到'9'表示從節點i到節點j需要耗費的時間。
Output
包含一個整數,可能的路徑數,這個數可能很大,只需輸出這個數除以2009的餘數。
Sample Input
【輸入樣例一】
2 2
11
00
【輸入樣例二】
5 30
12045
07105
47805
12024
12345
Sample Output
【輸出樣例一】
1
【樣例解釋一】
0->0->1
【輸出樣例二】
852
HINT
30%的資料,滿足 2 <= N <= 5 ; 1 <= T <= 30 。 100%的資料,滿足 2 <= N <= 10 ; 1 <= T <= 1000000000 。
這道題爆零了orz。。一般來說都會想到處理環(包括自環)吧。。於是打了個tarjan縮點,dfs求最短路,最後利用縮點得來的每個環路徑長短,進行拼湊T。失敗了orzorzorz,居然全錯。。
正解為矩陣乘法快速冪,可以知道這個圖本身就是一個矩陣,要是要走T次到達終點的話相當於每次自乘矩陣,每一條邊都是一種選擇的情況,先按矩陣乘法乘,那個加起來的值就是從一個點到另一個點的情況和。起點到終點答案就是f[1][n]。
但是這種情況只能處理每條邊邊權均為1的情況,若大於1則不能這麼處理了。
於是我們發現邊權只有0~9,0,1不需要考慮,2~9邊權的話就把一個點拆成2~9個點相連的邊權為1的邊,加在矩陣後面就好了,答案還是f[1][n]處取得。
計算怎麼加在後面詳細看程式碼就好了,就是那個calc函式。。
程式碼:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt putchar
#define gc getchar
#define ko pt(' ')
#define ex pt('\n')
const int MAXN = 150;
const ll MOD = 2009;
int n,m,sum,T;
int ans[MAXN][MAXN],a[MAXN][MAXN],b[MAXN][MAXN];
inline int calc(int x,int y) {return (y-1)*n+x;}
void in(int &x)
{
int num = 0,f = 1; char ch = gc();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
x = num*f;
}
void lin(ll &x)
{
ll num = 0,f = 1; char ch = gc();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
x = num*f;
}
void out(ll x)
{
if(x < 0) x = -x,pt('-');
if(x > 9) out(x/10);
pt(x % 10 + '0');
}
inline void mul()
{
memset(b,0,sizeof b);
for(int i = 1;i <= n*9;i++)
for(int j = 1;j <= n*9;j++)
for(int k = 1;k <= n*9;k++)
b[i][j] = (b[i][j] + ans[i][k]*a[k][j]) % MOD;
memcpy(ans,b,sizeof b);
}
inline void mulself()
{
memset(b,0,sizeof b);
for(int i = 1;i <= n*9;i++)
for(int j = 1;j <= n*9;j++)
for(int k = 1;k <= n*9;k++)
b[i][j] = (b[i][j] + a[i][k]*a[k][j]) % MOD;
memcpy(a,b,sizeof b);
}
inline void init()
{
in(n); in(T);
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
{
char ch = getchar();
int num = ch-'0';
if(!num) continue;
a[i][calc(j,num)] = 1;
}
for(int j = 2;j <= 9;j++) a[calc(i,j)][calc(i,j-1)] = 1;
getchar();
}
}
inline void work()
{
for(int i = 1;i <= n*9;i++) ans[i][i] = 1;
while(T)
{
if(T & 1) mul();
T >>= 1;
mulself();
}
out(ans[1][n] % MOD);
}
int main()
{
init();
work();
return 0;
}