D:D - Knight Tournament CodeForces - 356A
題意:編號1到n的人打架,給出m個區間, 編號位於這個區間中的人打架,再給出這個區間中最後勝利的人,對於每個人,求打敗他們的人的編號。
思路一:線段樹維護一個區間的勝者編號,由於每次只有第一次被打敗有效,因此要倒着遍歷區間,從這個區間的勝者的編號爲中點,分成兩個區間分別進行維護。但是我腦子卡,非要正着搞。
思路二(嫖的,大佬就是大佬,思路刁鑽):暴力的優化,如果正常暴力操作的話,我們每次都對每個區間給定的範圍都進行遍歷,判斷其中的每一個值是否被打敗過,沒有就更新,有就跳過,所以這樣會進行很多重複操作,導致複雜度O(n^2),所以爲了優化,已經得到答案的這些編號我們想辦法去除掉,後來再進行更新的時候就用管這些值了。用set來表示未更新答案的集合,每更新一個答案就去除一個,同時記錄下答案就行。
//set暴力優化
#include<bits/stdc++.h>
#define mod (10007)
#define middle (l+r)>>1
#define SIZE 1000000+5
#define lowbit(x) (x&(-x))
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
typedef long double ld;
const int inf_max = 0x3f3f3f;
const ll Linf = 9e18;
const int maxn = 3e5+5;
const long double E = 2.7182818;
const double eps=0.0001;
using namespace std;
inline int read()
{
int f=1,res=0;
char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { res=res*10+ch-'0' ; ch=getchar(); }
return f*res;
}
struct MATCH
{
int l,r,x;
}match[maxn];
int n,m,ans[maxn];
set<int>st;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) st.insert(i);
for(int i=1;i<=m;i++) {
int l,r,w;
scanf("%d%d%d",&l,&r,&w);
set<int>::iterator iter=st.lower_bound(l);
while((*iter)<=r&&iter!=st.end()) {
ans[(*iter)]=w;
//cout<<(*iter)<<" "<<w<<endl;
iter=st.erase(iter);
}
st.insert(w);
}
set<int>::iterator iter=st.begin();
ans[(*iter)]=0;
for(int i=1;i<=n;i++) {
printf("%d%c",ans[i],i==n?'\n':' ');
}
return 0;
}
F - Fox And Jumping
題意:用最少的花費選出一些卡片使得它們的最大gcd爲1;
思路:看出來了感覺還可以,就是l太大了不好開dp數組,在這裏學到了一種操作:用map開dp數組就不會曝內存.
dp[i][j]:前i個數最大公因數爲j的最小cost;
dp[i][gcd(j,l[i])]=min(dp[i-1][j]+cost[i]); 其中l[i]是第i張卡片的長度。
#include<bits/stdc++.h>
#define mod (10007)
#define middle (l+r)>>1
#define SIZE 1000000+5
#define lowbit(x) (x&(-x))
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
typedef long double ld;
const int inf_max = 0x3f3f3f;
const ll Linf = 9e18;
const int maxn = 300+5;
const long double E = 2.7182818;
const double eps=0.0001;
using namespace std;
inline int read()
{
int f=1,res=0;
char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { res=res*10+ch-'0' ; ch=getchar(); }
return f*res;
}
int n,l[maxn],c[maxn];
map<int,int>dp;
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int main()
{
dp.clear();
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&l[i]);
for(int i=1;i<=n;i++) scanf("%d",&c[i]);
for(int i=1;i<=n;i++) {
dp[l[i]]=c[i]; //這裏初始化要注意一下,因爲我們可以從任意一個數開始選,這裏的意思就是我們就只選當前這個數
//if(i==1) continue;
for(auto j:dp) {
int t=gcd(j.first,l[i]);
if(dp.count(t) == 0) dp[t]=j.second+c[i];
dp[t]=min(dp[t],j.second+c[i]);
}
}
if(dp.count(1)) cout<<dp[1]<<endl;
else cout<<-1<<endl;
return 0;
}
E - Destroying the bus stations HDU - 2485
題意:給n個點m條邊的有向圖和一個參數k,問最少刪幾個點可以讓從1~n的最短路所花的時間大於k。
思路:這題好像是最大流什麼來做,但是學長說這個方法是錯誤的,正確方法應該是暴力枚舉+spfa。還沒學最大流,也看不出來這個題對不對T~T。暴力枚舉刪除的點然後看剩下的點的最短路能否大於k,可以的話就是答案;這裏的暴力採取的是迭代加深的方法,以前看過,不過感覺沒怎麼用,給忘了 我g(果然還是做的題太少了)。這個迭代加深就好像bfs+dfs 在縱向找答案的時候,也橫向找,但是規定一個縱向的深度,到這個深度還沒有找到就返回false;然後給下一個深度來繼續枚舉。這樣做的好處就是,因爲dfs會一直找到盡頭,然而我們的答案可能在某個深度橫着找一下就有了,所以我們限制着深度同時橫着找。迭代加深適合的數據量較小,這道題可以這樣我也是醉了。
其次我們只刪除最短路上的點,因爲其他的點刪了對答案也不會影響。
還有一點就是在跑spfa的時候,由於路徑長度是1,所以我們只要遍歷到了一個點,那麼這條路徑就一定是最短的了,所以一個點入隊一次就行了,這個就像樹的層次遍歷,只不過,樹裏面兩點間只有一條路徑所以只需要跑一次。
#include<bits/stdc++.h>
#define mod (10007)
#define middle (l+r)>>1
#define SIZE 1000000+5
#define lowbit(x) (x&(-x))
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
typedef long double ld;
const int inf_max = 0x3f3f3f;
const ll Linf = 9e18;
const int maxn = 5000+5;
const long double E = 2.7182818;
const double eps=0.0001;
using namespace std;
inline int read()
{
int f=1,res=0;
char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { res=res*10+ch-'0' ; ch=getchar(); }
return f*res;
}
int n,m,k,head[100],vis[100],cnt,del[100],dis[100],pre[100],path[100][100];
struct EDGE
{
int v,last;
}edge[maxn];
void Initial()
{
memset(edge,0,sizeof(edge));
cnt=0;
memset(head,-1,sizeof(head));
}
void add_edge(int u,int v)
{
edge[cnt].v=v;
edge[cnt].last=head[u];
head[u]=cnt++;
}
void SPFA(int start)
{
memset(pre,-1,sizeof(pre));
memset(dis,inf_max,sizeof(dis));
memset(vis,0,sizeof(vis));
queue<int>q;
while(!q.empty()) q.pop();
dis[start]=0;pre[start]=-1;
q.push(start);vis[start]=1;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=head[u];~i;i=edge[i].last) {
int v=edge[i].v;
if(del[v]||vis[v]) continue; //如果這個點被刪了或者這個點已經跑過了,就跳過;
dis[v]=dis[u]+1;
pre[v]=u; //路徑的保存;
q.push(v);vis[v]=1;
}
}
}
bool dfs(int dep,int limit)
{
SPFA(1);
if(dis[n]>k) return true; //達到條件返回true
if(dep==limit) return false; //規定深度到達還未滿足條件,返回false
int j=n;
while(j!=-1) {
path[dep][j]=pre[j];
j=pre[j];
}//因爲在下個深度時跑spfa會把pre清空,所以我們要把每個深度的路徑保留下來,方便在每一層對路徑的刪除進行操作
int i=path[dep][n];
bool flag=false;
while(i!=1) { //枚舉刪除的點
if(!del[i]) {
del[i]=1; //路徑刪掉
flag=(flag||dfs(dep+1,limit));
del[i]=0; //路徑復原
}
i=path[dep][i];
}
return flag;
}
int main()
{
while(scanf("%d%d%d",&n,&m,&k),n+m+k)
{
Initial();
for(int i=1;i<=m;i++) {
int u,v;
scanf("%d%d",&u,&v);
add_edge(u,v);
}
for(int i=0;i<=n;i++) {
memset(del,0,sizeof(del));
memset(path,0,sizeof(path));
if(dfs(0,i)) {
printf("%d\n",i);
break;
}
}
}
return 0;
}